单链表(增删改查)
数据结构:单链表(增删改查)
目录
准备一个结点类
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;
}
}
准备一个链表类
class SingleLinkedList {
//先初始化头结点
private HeroNode head = new HeroNode(0, "", "");
//定义一个尾结点,用于添加结点
private HeroNode tail = head;
}
add()
public void add(HeroNode heroNode) {
tail.next = heroNode;
tail = heroNode;
}
按照结点编号升序插入链表
public void addByOrder(HeroNode heroNode) {
HeroNode temp = head; //temp指向要比较的结点的前一个结点
boolean flag = false; //标志添加的编号是否存在,默认false
//寻找添加位置
while (true) {
if (temp.next == null) {//没找到插入位置,直接插入到链表尾
break;
}
if (temp.next.no > heroNode.no) {//下一个结点no大于插入的no,前一个结点no小于插入的no
break;
} else if (temp.next.no == heroNode.no) {//说明希望添加的heroNode的编号已经存在
flag = true;
}
temp = temp.next;//后移
}
//判断flag的值
if (flag) {//编号存在,不能添加
System.out.printf("准备插入的英雄编号%d 已经存在了,不能加入\n", heroNode.no);
} else {//编号不存在,插入到链表中,temp指针后面
heroNode.next = temp.next;
temp.next = heroNode;
}
}
注意
如果链表中已经存在要插入的结点,则不能插入,并给出提示
update(HeroNode newHeroNode)
public void update(HeroNode newHeroNode) {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空!");
return;
}
//找到需要修改的结点,根据no编号
//定义遍历指针
HeroNode temp = head;
while (temp != null && temp.no != newHeroNode.no) {
temp = temp.next;
}
if (temp == null) {
System.out.printf("没有找到编号 %d 的结点,不能修改\n", newHeroNode.no);
} else {
temp.name = newHeroNode.name;
temp.nickName = newHeroNode.nickName;
}
}
del(int no)
思路:找到待删除结点的前一个结点,temp指向要比较的结点的前一个位置,所以比较时是,temp.next.no == delNo
public void del(int no) {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空!");
return;
}
//定义指针
HeroNode temp = head;
while (temp.next != null && temp.next.no != no) {
temp = temp.next;
}
if (temp.next == null) {//没有找到删除结点,temp就移动到了链尾
System.out.printf("没有找到编号 %d 的结点,不能删除\n", no);
} else {//找到删除结点,temp指向链表中间
temp.next = temp.next.next;
}
}
打印链表
public void list() {
//先判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode temp = head.next;
while (temp != null) {
//输出结点信息
System.out.println(temp);
//将temp后移
temp = temp.next;
}
}
基本面试题
获取链表结点个数
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;
}
获取单链表中的倒数第k个结点
思路:
1. 先获取链表长度length
2. 得到length后,指针只需从第一个结点向后移动 length-index 次
public static HeroNode getLastIndexOf(HeroNode head, int index) {
if (head.next == null) {
return null;
}
int length = getLength(head);
if (index <= 0 || index > length) {
return null;
}
HeroNode cur = head;
for (int i = 0; i <= length - index; i++) {
cur = cur.next;
}
return cur;
}
反转链表
遍历法
思路:
1. 创建一个新的头节点
2. 遍历原有链表,将遍历到的每个结点插入到新创建的结点的后面
public static void reverseList(HeroNode head) {
//如果链表为空或者链表只有一个结点,就无需反转,直接返回
if (head.next == null || head.next.next == null) {
return;
}
HeroNode reverseHead = new HeroNode(0, "", "");
HeroNode cur = head.next;
//临时保存要插入反转链表的结点
HeroNode curNext;
//遍历原来的链表,每遍历一个结点,就将这个结点插入到新链表的头部
while (cur != null) {
curNext = cur.next;
cur.next = reverseHead.next;
reverseHead.next = cur;
cur = curNext;
}
head.next = reverseHead.next;
}
利用Stack实现
思路:
1. 遍历结点,并压入栈中
2. 循环取出栈顶结点,并将其next指向新的栈顶结点
public static void reverse(HeroNode head) {
if (head.next == null || head.next.next == null) {
return;
}
Stack<HeroNode> stack = new Stack<>();
HeroNode cur = head.next;
//遍历结点并压入栈中
while (cur != null) {
stack.push(cur);
cur = cur.next;
}
//先取出一个结点,使next指向栈顶的结点
head.next = stack.pop();
head.next.next = stack.peek();
while (stack.size() > 0) {
//循环取出结点,并next指向栈顶结点,最后一个结点指向null
HeroNode node = stack.pop();
if (stack.size() == 0) {
node.next = null;
break;
}
node.next = stack.peek();
}
}
逆序打印链表
思路:
1. 同样是借助栈,遍历结点压入栈
2. 取出栈顶结点并打印
public static void reversePrint(HeroNode head) {
if (head.next == null) {
return;
}
Stack<HeroNode> stack = new Stack<>();
HeroNode cur = head.next;
//将链表所有结点压入栈中,需要注意的是,jdk1.8,栈的最大深度为12000
while (cur != null) {
stack.push(cur);
cur = cur.next;
}
//循环出栈,打印出栈的结点
while (stack.size() > 0) {
System.out.println(stack.pop());
}
}
合并两个有序的链表,新的链表同样有序
思路:
-
创建一个新的头结点
-
从头到尾遍历两个链表,比较两链表中指针指向的结点大小,小的先插入新的链表中,然后移动指针
取下一个结点和另外一个链表中指针指向的结点比较
-
一旦有一个链表遍历完成,则判断哪个链表不为空,就将新链表的尾部指向不为空链表中指针指向的下一个结点
注意:
如果有一个链表为空,就将新链表头结点指向这个链表的第一个结点
如果两个链表都为空,就直接指向null返回
public static HeroNode concatList(HeroNode head1, HeroNode head2) {
HeroNode newHead = new HeroNode(0, "", "");
if (head1.next == null && head2.next == null) {
newHead.next = null;
return newHead;
}
HeroNode tail = newHead;
if (head1.next == null && head2.next != null) {
newHead.next = head2.next;
}
if (head1.next != null && head2.next == null) {
newHead.next = head1.next;
}
HeroNode cur1 = head1.next;
HeroNode cur2 = head2.next;
while (cur1 != null && cur2 != null) {
if (cur1.no <= cur2.no) {
tail.next = cur1;
cur1 = cur1.next;
} else {
tail.next = cur2;
cur2 = cur2.next;
}
tail = tail.next;
}
if (cur1 == null) {
tail.next = cur2;
} else {
tail.next = cur1;
}
return newHead;
}