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

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

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

1 2 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节点类成员属性。

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

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

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

1 2 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;

1 2 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; }
|

测试

1 2 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
则正常在尾部添加

1 2 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; }
|

测试

1 2 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;

1 2 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("链表中没有该编号节点信息!"); }
|

测试

1 2 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,报空指针异常。

1 2 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("要删除的节点不存在!"); }
|

测试

1 2 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();
}
|

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

1 2 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; }
|

测试
1 2 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+=

1 2 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; }
|

链表反转
将一条链表反转,要创建一个新的节点来进行操作。详情看图!
1 2 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; }
|


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

1 2 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()); } }
|