手写单向链表
定义:
单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始;链表是使用指针进行构造的列表;又称为结点列表,因为链表是由一个个结点组装起来的;其中每个结点都有指针成员变量指向列表中的下一个结点;
链表是由结点构成,head指针指向第一个成为表头结点,而终止于最后一个指向NULL的指针。
单向链表示意图:

单向链表的特点:
优点:
1 链表结构在物理空间中不需要连续的,逻辑上连续的一种结构。
2 单个结点创建非常方便,不需要开辟连续的物理空间,只需要有单个结点的空间就可以创建链表,之后再添加新元素时可以使用任何位置的空间,可以使用与现在元素的位置不相连接的空间。
3 插入或删除的效率较数组更好,不需要重新移动现在数据。
缺点:
查询的效率较数组差。必须依次按钮链表的顺序进行查询,直到查询到指定的元素为止。
手写单向链表
创建节点类
class HeroNode {
int no;
String name;
String nickName;
HeroNode next;
private static HeroNode instance;
public HeroNode() {
}
public HeroNode(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
public static HeroNode getInstance() {
if (instance == null){
synchronized (HeroNode.class){
if (instance == null){
instance = new HeroNode(0, "", "");
}
}
}
return instance;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
创建链表类
class SingleLinkedList {
public SingleLinkedList() {
head = HeroNode.getInstance();
}
private HeroNode head;
/**
* 展示列表
*/
public void showList() {
if (head.next == null) {
System.out.println("链表为空~~~");
return;
}
HeroNode temp = head.next;
while (true) {
System.out.println(temp);
if (temp.next == null) {
break;
}
temp = temp.next;
}
}
/**
* 在链表尾部添加元素
*
* @param node
*/
public void add(HeroNode node) {
HeroNode temp = head;
while (true) {
if (temp.next == null) {
temp.next = node;
break;
}
temp = temp.next;
}
}
/**
* 根据英雄编号有序插入相应位置
*
* @param node
*/
public void insert(HeroNode node) {
if (head.next == null) {
head.next = node;
return;
}
HeroNode temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) {
flag = true;
break;
}
if (node.no == temp.next.no) {
break;
}
if (temp.next.no > node.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
node.next = temp.next;
temp.next = node;
} else {
System.out.println("当前英雄编号已存在,不能插入");
}
}
/**
* 根据编号删除对应编号的英雄
*
* @param no 英雄编号
*/
public void delete(int no) {
if (head.next == null) {
System.out.printf("链表不存在对应编号为 %d 的英雄", no);
return;
}
boolean flag = false;
HeroNode temp = this.head;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.no == no) {
flag = true;
temp.next = temp.next.next;
break;
}
temp = temp.next;
}
if (flag) {
System.out.printf("删除编号为 %d 的英雄\n", no);
} else {
System.out.println("链表不存在对应编号的英雄");
}
}
}
测试:
1 在尾部添加元素:
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
// 在尾部添加元素
list.add(hero1);
list.add(hero4);
list.add(hero2);
list.add(hero3);
list.showList();
}
测试结果:
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
单向链表元素的顺序是按照我们添加的顺序排列的,而没有按照序号进行排序,我们进行优化,使用insert方法可以实现添加新元素时按照序号插入到指定的位置。
2 有序插入元素
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
// 按照编号有序插入元素
list.insert(hero1);
list.insert(hero4);
list.insert(hero2);
list.insert(hero3);
list.showList();
}
测试结果:
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
3 删除指定元素
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
// 按照编号有序插入元素
list.insert(hero1);
list.insert(hero4);
list.insert(hero2);
list.insert(hero3);
System.out.println("删除前的链表");
list.showList();
list.delete(1);
list.delete(4);
System.out.println("删除后的链表");
list.showList();
}
测试结果:
删除前的链表
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
删除编号为 1 的英雄
删除编号为 4 的英雄
删除后的链表
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
面试题
1 获取单向链表中元素的个数
SingleLinkedList类中添加方法
/**
* 获取链表的元素的个数
*
* @return
*/
public int getNumberOfList() {
int number = 0;
HeroNode temp = head.next;
while (temp != null) {
number++;
temp = temp.next;
}
return number;
}
测试代码
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
// 按照编号有序插入元素
list.insert(hero1);
list.insert(hero4);
list.insert(hero2);
list.insert(hero3);
// 获取单向链表元素的个数
System.out.println("单向链表元素的个数为:" + list.getNumberOfList());
}
测试结果:
单向链表元素的个数为:4
2 获取单向链表的倒数第k个元素
SingleLinkedList类中添加方法
/**
* 获取倒数第k个元素
*
* @param k
*/
public void getHeroNodeByCountBackwards(int k) {
if (head.next == null) {
System.out.println("该列表不存在指定倒数序号的英雄");
return;
}
int number = this.getNumberOfList();
HeroNode temp = this.head.next;
if (number < k || k <= 0) {
System.out.println("该列表不存在指定倒数序号的英雄");
return;
}
for (int j = 0; j < number - k; j++) {
temp = temp.next;
}
System.out.println("该列表倒数第 " + k + " 的英雄为: " + temp);
}
测试代码:
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
// 按照编号有序插入元素
list.insert(hero1);
list.insert(hero4);
list.insert(hero2);
list.insert(hero3);
// 获取倒数第k个元素
list.getHeroNodeByCountBackwards(2);
}
测试结果:
该列表倒数第 2 的英雄为: HeroNode{no=3, name='吴用', nickName='智多星'}
3 反转单向链表
SingleLinkedList类中添加方法
/**
* 反转单向链表
*/
public void inversion() {
if (head.next == null) {
return;
}
// 思路:
// 1.创建一个新的单向链表头inversionHead,
// 2.将原链表中的元素以一个一个取出,并存入新链表的首个元素的位置,
// 3.将head.next = inversionHead.next
HeroNode inversionHead = new HeroNode(0, "", "");
HeroNode temp = head.next;
HeroNode first;
while (temp != null) {
first = inversionHead.next;
inversionHead.next = temp;
head.next = temp.next;
temp.next = first;
temp = head.next;
}
head.next = inversionHead.next;
}
测试代码:
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
// 按照编号有序插入元素
list.insert(hero1);
list.insert(hero4);
list.insert(hero2);
list.insert(hero3);
System.out.println("==============反转前的列表================");
list.showList();
// 反转列表
list.inversion();
System.out.println("==============反转后的列表================");
list.showList();
}
测试结果:
==============反转前的列表================
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
==============反转后的列表================
HeroNode{no=4, name='林冲', nickName='豹子头'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=1, name='宋江', nickName='及时雨'}
4 倒序打印单向链表,并不改变链表结构
SingleLinkedList类中添加方法
/**
* 倒序打印单向链表,并不改变链表结构
* 使用Stack
*/
public void inversionPrint() {
if (head.next == null) {
System.out.println("单向链表为空");
return;
}
HeroNode temp = head.next;
Stack<HeroNode> stack = new Stack<>();
while (temp != null) {
stack.push(temp);
temp = temp.next;
}
while (stack.size() > 0) {
System.out.println(stack.pop());
}
}
测试代码:
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
// 按照编号有序插入元素
list.insert(hero1);
list.insert(hero4);
list.insert(hero2);
list.insert(hero3);
System.out.println("==============倒序打印前的链表================");
list.showList();
// 倒序打印链表
System.out.println("================倒序打印链表==================");
list.inversionPrint();
System.out.println("==============倒序打印后的链表================");
list.showList();
}
测试结果:
==============倒序打印前的链表================
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
================倒序打印链表==================
HeroNode{no=4, name='林冲', nickName='豹子头'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=1, name='宋江', nickName='及时雨'}
==============倒序打印后的链表================
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
5 合并两个有序单向链表,合并后的单向链表依然是有序的
SingleLinkedList类中添加方法
/**
* 合并两个有序单向链表,合并后的链表也是有序的
* @param list1 单向列表1
* @param list2 单向列表2
* @return 合并后的有序单向链表
*/
public static SingleLinkedList mergeList(SingleLinkedList list1, SingleLinkedList list2) {
SingleLinkedList list = new SingleLinkedList();
HeroNode temp1 = list1.head.next;
HeroNode temp2 = list2.head.next;
HeroNode temp;
HeroNode newListHead = list.head;
HeroNode last = newListHead;
boolean flag = true;
while (temp1 != null || temp2 != null) {
if (temp1 != null && temp2 != null){
if (temp1.no < temp2.no){
flag = true;
temp = temp1;
}else{
flag = false;
temp = temp2;
}
}else if (temp1 != null){
temp = temp1;
flag = true;
}else {
temp = temp2;
flag = false;
}
last.next = temp;
if (flag){
list1.head.next = temp1.next;
}else{
list2.head.next = temp2.next;
}
temp.next = null;
last = last.next;
temp1 = list1.head.next;
temp2 = list2.head.next;
}
return list;
}
测试代码:
@Test
public void test1() {
SingleLinkedList list1 = new SingleLinkedList();
SingleLinkedList list2 = new SingleLinkedList();
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "公孙胜", "入云龙");
HeroNode hero5 = new HeroNode(5, "关胜", "大刀");
HeroNode hero6 = new HeroNode(6, "林冲", "豹子头");
// 按照编号有序插入元素
list1.insert(hero3);
list1.insert(hero5);
list1.insert(hero2);
list2.insert(hero1);
list2.insert(hero4);
list2.insert(hero6);
System.out.println("======单向链表1=====");
list1.showList();
System.out.println("======单向链表2=====");
list2.showList();
SingleLinkedList mergeList = SingleLinkedList.mergeList(list1, list2);
System.out.println("======合并后的单向链表=====");
mergeList.showList();
}
测试结果:
======单向链表1=====
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=5, name='关胜', nickName='大刀'}
======单向链表2=====
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=4, name='公孙胜', nickName='入云龙'}
HeroNode{no=6, name='林冲', nickName='豹子头'}
======合并后的单向链表=====
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='公孙胜', nickName='入云龙'}
HeroNode{no=5, name='关胜', nickName='大刀'}
HeroNode{no=6, name='林冲', nickName='豹子头'}
源码地址:
https://gitee.com/wang-hailei/arithmetic/tree/main
本文详细介绍了如何手写单向链表,包括链表的定义、特点及优缺点。通过创建节点类和链表类实现链表操作,如在尾部添加元素、有序插入元素和删除指定元素。此外,还提供了常见的链表面试题,如获取链表长度、倒数第k个元素、反转链表、倒序打印链表以及合并两个有序链表的解决方案,并附有测试结果。
1189

被折叠的 条评论
为什么被折叠?



