简介
概念介绍
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
以“结点的序列”表示线性表称作线性链表(单链表),单链表是链式存取的结构。
链接存储方法
链接方式存储的线性表简称为链表(Linked List)。
链表的具体存储表示为:
① 用一组任意的存储单元来存放线性表的结点(这组存储单元既可以是连续的,也可以是不连续的)
② 链表中结点的逻辑次序和物理次序不一定相同。为了能正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其后继结点的地址(或位置)信息(称为指针(pointer)或链(link))
链式存储是最常用的存储方式之一,它不仅可用来表示线性表,而且可用来表示各种非线性的数据结构。
结点结构
┌───┬───┐
│   data│next   |
└───┴───┘
data域–存放结点值的数据域
next域–存放结点的直接后继的地址(位置)的指针域(链域)
链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的,每个结点只有一个链域的链表称为单链表(Single Linked List)。
单链表中每个结点的存储地址是存放在其前趋结点next域中,而开始结点无前趋,故应设头指针head指向开始结点。链表由头指针唯一确定,单链表可以用头指针的名字来命名。
终端结点无后继,故终端结点的指针域为空,即NULL。
图示
带单独头节点单链表的逻辑结构示意图

不带单独头节点的单链表逻辑结构示意图

代码实现
使用带head头的单向链表实现 –水浒英雄排行榜管理。图示省略nickname
定义节点类
创建HeroNode类

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 
 | class HeroNode {
 public int no;
 public String name;
 public String nickname;
 public HeroNode next;
 
 
 public HeroNode(int no, String name, String nickname) {
 this.no = no;
 this.name = name;
 this.nickname = nickname;
 }
 
 
 @Override
 public String toString() {
 return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
 }
 }
 
 | 
创建单链表类
定义SingleLinkedList 管理我们的英雄,创建一个head节点类成员属性。

| 12
 
 | private HeroNode head = new HeroNode(0, "", "");
 
 | 

获取到头节点
直接将单链表类中定义的head返回return head;

| 12
 3
 4
 
 | public HeroNode getHead() {
 return head;
 }
 
 | 
遍历单链表
首先判断单链表是否为空head.next=null,不为空依次遍历节点

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | public void list() {
 
 if (head.next == null) {
 System.out.println("链表为空");
 return;
 }
 
 HeroNode temp = head.next;
 
 while (temp != null) {
 
 System.out.println(temp);
 
 temp = temp.next;
 }
 }
 
 | 

添加节点
遍历单链表,在最后一个节点添加入新的节点。HerNode temp = head;

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | 
 
 
 public void add(HeroNode heroNode) {
 
 HeroNode temp = head;
 
 while (temp.next != null) {
 
 temp = temp.next;
 }
 
 
 temp.next = heroNode;
 }
 
 | 

测试

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | 
 HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
 HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
 HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
 HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
 
 
 SingleLinkedList singleLinkedList = new SingleLinkedList();
 
 
 singleLinkedList.add(hero1);
 singleLinkedList.add(hero2);
 singleLinkedList.add(hero3);
 singleLinkedList.add(hero4);
 
 singleLinkedList.list();
 
 | 
添加节点,遍历单链表成功!
排序添加节点
添加节点的时候根据节点的编号按照从小到大的顺序依次添加节点HerNode temp = head;由于是单链表所以为们要通过temp.next才能进行比较节点。如果temp.next.no>heroNode.no则进行相应的操作。temp.next.no==heroNode.no则序号相同不添加。temp.next.no<heroNode.no则正常在尾部添加

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | 
 public void addByOrder(HeroNode heroNode) {
 
 
 HeroNode temp = head;
 while (temp.next != null) {
 if (temp.next.no > heroNode.no) {
 
 heroNode.next = temp.next;
 temp.next = heroNode;
 return;
 } else if (temp.next.no == heroNode.no) {
 System.out.printf("准备插入的英雄的编号 %d 已经存在了, 不能加入\n", heroNode.no);
 return;
 }
 temp = temp.next;
 }
 
 temp.next = heroNode;
 }
 
 | 

测试

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | public class SingleLinkedListDemo {public static void main(String[] args) {
 
 
 HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
 HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
 HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
 HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
 
 
 SingleLinkedList singleLinkedList = new SingleLinkedList();
 
 
 
 
 
 
 
 
 singleLinkedList.addByOrder(hero1);
 singleLinkedList.addByOrder(hero4);
 singleLinkedList.addByOrder(hero2);
 singleLinkedList.addByOrder(hero3);
 
 
 singleLinkedList.list();
 
 }
 
 | 
使用排序添加节点,遍历单链表。排序成功!

修改节点
首先判断链表是否为空,不为空根据节点的序号修改节点信息,若通过序号没有匹配到该节点,提示信息。`HeroNode temp = head.next;

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | public void update(HeroNode newHeroNode) {
 
 if (head.next == null) {
 System.out.println("链表为空");
 return;
 }
 HeroNode temp = head.next;
 while (temp != null) {
 
 if (temp.no == newHeroNode.no) {
 temp.name = newHeroNode.name;
 temp.nickname = newHeroNode.nickname;
 return;
 }
 temp = temp.next;
 }
 System.out.println("链表中没有该编号节点信息!");
 }
 
 | 

测试

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 
 |     public static void main(String[] args) {
 
 HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
 HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
 HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
 HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
 
 
 SingleLinkedList singleLinkedList = new SingleLinkedList();
 
 
 
 
 
 
 
 
 singleLinkedList.addByOrder(hero1);
 singleLinkedList.addByOrder(hero4);
 singleLinkedList.addByOrder(hero2);
 singleLinkedList.addByOrder(hero3);
 
 
 singleLinkedList.list();
 
 
 HeroNode newHeroNode = new HeroNode(2, "小卢", "玉麒麟~~");
 singleLinkedList.update(newHeroNode);
 
 System.out.println("修改后的链表情况~~");
 singleLinkedList.list();
 
 }
 
 | 

修改节点信息成功!
删除节点
首先判断链表是否为空,不为空根据序号删除节点,序号不存在提示错误信息!while循环条件应为temp.next != null否则当遍历到最后一个节点时temp.next为null,报空指针异常。

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | public void del(int no) {
 if (head.next == null) {
 System.out.println("当前链表为空!");
 }
 
 HeroNode temp = head;
 
 while (temp.next != null) {
 
 if (temp.next.no == no) {
 temp.next = temp.next.next;
 return;
 }
 
 temp = temp.next;
 }
 System.out.println("要删除的节点不存在!");
 }
 
 | 

测试

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 
 |     public static void main(String[] args) {
 
 HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
 HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
 HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
 HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
 
 
 SingleLinkedList singleLinkedList = new SingleLinkedList();
 
 
 
 
 
 
 
 
 singleLinkedList.addByOrder(hero1);
 singleLinkedList.addByOrder(hero4);
 singleLinkedList.addByOrder(hero2);
 singleLinkedList.addByOrder(hero3);
 
 
 singleLinkedList.list();
 
 
 HeroNode newHeroNode = new HeroNode(2, "小卢", "玉麒麟~~");
 singleLinkedList.update(newHeroNode);
 
 System.out.println("修改后的链表情况~~");
 singleLinkedList.list();
 
 
 singleLinkedList.del(1);
 singleLinkedList.del(4);
 System.out.println("删除后的链表情况~~");
 singleLinkedList.list();
 
 }
 
 | 

链表长度
传入链表头节点获取链表的有效长度。

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | 
 
 
 
 
 public static int getLength(HeroNode head) {
 if (head.next == null) {
 return 0;
 }
 int length = 0;
 
 HeroNode cur = head.next;
 while (cur != null) {
 length++;
 cur = cur.next;
 }
 return length;
 }
 
 | 

 测试
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 
 |  public static void main(String[] args) {
 
 HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
 HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
 HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
 HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
 
 
 SingleLinkedList singleLinkedList = new SingleLinkedList();
 
 
 
 
 
 
 
 
 singleLinkedList.addByOrder(hero1);
 singleLinkedList.addByOrder(hero4);
 singleLinkedList.addByOrder(hero2);
 singleLinkedList.addByOrder(hero3);
 
 
 singleLinkedList.list();
 
 
 HeroNode newHeroNode = new HeroNode(2, "小卢", "玉麒麟~~");
 singleLinkedList.update(newHeroNode);
 
 System.out.println("修改后的链表情况~~");
 singleLinkedList.list();
 
 
 singleLinkedList.del(1);
 singleLinkedList.del(4);
 System.out.println("删除后的链表情况~~");
 singleLinkedList.list();
 
 
 System.out.println("有效的节点个数=" + getLength(singleLinkedList.getHead()));
 
 
 }
 
 | 

查找倒数k节点
根据传入的位置在链表中查找节点。首先判断链表是否为空,接着判断传入的位置的合法性。在获取到链表的总共长度。int i = 0;i <size - index; i+=

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | public static HeroNode findLastIndexNode(HeroNode heroNode, int index) {
 if (heroNode.next == null) return null;
 
 
 int size = getLength(heroNode);
 
 if (index <= 0 || index > size) return null;
 
 HeroNode node = heroNode.next;
 for (int i = 0; i < size - index; i++) {
 node = node.next;
 }
 return node;
 }
 
 | 

链表反转
将一条链表反转,要创建一个新的节点来进行操作。详情看图!
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 | public static void reversetList(HeroNode head) {
 
 if (head.next == null || head.next.next == null) {
 return;
 }
 
 
 HeroNode cur = head.next;
 HeroNode next = null;
 HeroNode reverseHead = new HeroNode(0, "", "");
 
 
 while (cur != null) {
 next = cur.next;
 cur.next = reverseHead.next;
 reverseHead.next = cur;
 cur = next;
 }
 
 head.next = reverseHead.next;
 }
 
 | 


逆序打印
这里使用到了栈结构,你也可以使用其他的方法实现。

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | public static void reversePrint(HeroNode head) {
 if (head.next == null) {
 return;
 }
 
 Stack<HeroNode> stack = new Stack<HeroNode>();
 HeroNode cur = head.next;
 
 while (cur != null) {
 stack.push(cur);
 cur = cur.next;
 }
 
 while (stack.size() > 0) {
 System.out.println(stack.pop());
 }
 }
 
 |