写在前面
单链表
- 有序的链表,一个节点包括
数据域(data)和包含下一个数组的地址域(next)
- 各个节点不一定是连续存放/存储的
- 分为带头节点的列表/没有头节点的链表
- 单向链表查找的方向只有一个;不能实现自我删除,需要辅助节点
单链表实现
class Node {
public int id;
public String name;
public Node next;
public Node(int id, String name) {
this.id = id;
this.name = name;
}
}
class SingleLinkedList {
private Node head = new Node(0,"");
}
- 添加,不考虑编号顺序,找到当前链表的最后节点,将最后这个节点的next指向新节点,通过
temp.next == null来判断,是否为最后一个节点
public void add(Node node) {
//头节点不能动,通过辅助节点进行遍历
Node temp = head
while (true) {
//最后一个节点的next指向空
if (temp.next == null) {
break
}
//如果没有找到最后,就将temp后移
temp = temp.next
}
//当退出while循环,temp指向链表的最后
temp.next = node
}
- 按照顺序添加,断开要插入位置的前一个节点与后一个节点的连接,并将后一个节点连接上要插入的节点;
node.next = temp.next;;再连接前一个节点与要插入的节点temp.next = node;
public void addByOrder(Node node) {
//通过辅助变量找到插入的位置,位于添加位置的前一个节点
Node temp = head
boolean flag = false
while (true) {
//说明temp已经在最后了
if (temp.next == null) {
break
}
//找到位置,这个位置的后一个id要大于要添加的id
if (temp.next.id > node.id) {
break
} else if (temp.next.id == node.id) {
//添加的id已经存在,不能添加
flag = true
break
}
//后移
temp = temp.next
}
if (flag) {
System.out.println("准备插入的节点已经存在,修改名字")
temp.next.name = node.name
} else {
//插入到链表中 例如 1 3,2要插入,1是temp,2的下一个节点指向3,1的下一个节点指向2
//要插入的节点的下一个节点指向temp的下一个节点
node.next = temp.next
//找到的位置要指向要插入的节点
temp.next = node
}
}
public void update(Node node) {
if (head.next == null) {
System.out.println("List is empty")
return
}
Node temp = head
boolean flag = false
while (true) {
if (temp == null) {
break
}
if (temp.id == node.id) {
//找到节点
flag = true
break
}
temp = temp.next
}
if (flag) {
temp.name = node.name
} else {
System.out.println("node - " + node.id + " does not exist.")
}
}
- 删除,head不能动,通过temp找到要删除的节点的前一个节点;待删除的节点的前一个节点
temp的下一个节点,指向待删除节点temp.next的下一个节点temp.next.next
public void del(int id) {
Node temp = head
boolean flag = false
while (true) {
//已经到了链表的最后且没有找到
if (temp.next == null) {
break
}
if (temp.next.id == id) {
//找到待删除的节点的前一个节点
flag = true
break
}
temp = temp.next
}
if (flag) {
temp.next = temp.next.next
} else {
System.out.println("没有找到id为" + id + "的节点")
}
}
public void list() {
if (head.next == null) {
System.out.println("List is empty");
return;
}
Node temp = head.next;
while (true) {
if (temp == null) {
break;
}
System.out.println(temp);
temp = temp.next;
}
}
单链表面试题
求单链表中有效节点的个数
//获取单链表节点个数,如果带头节点的链表,不统计头节点
public static int getLength(Node head) {
if (head.next == null) {
//空链表
return 0
}
int length = 0
Node cur = head.next
while (cur != null) {
length ++
cur = cur.next
}
return length
}
查找倒数第K个节点
- 假设长度为3,查找倒数第2个,也就是正数第二个,
cur需要从头head.next移动1次,也就是正数移动size-index
public static Node findLastIndexNode(Node head, int index) {
if (head.next == null) { //空
return null
}
//得到链表长度
int size = getLength(head)
//第二次遍历获取目标
if (index <= 0 || index > size) {
return null
}
Node cur = head.next
for (int i = 0
cur = cur.next
}
return cur
}
单链表反转
- 当前
cur.next指向reverseHead.next,reverseHead.next指向cur,使cur插入reverseHead与reverseHead.next之间;从头到尾遍历原链表,就可以把后面的不断提前;next保存原链表中的下一个,等cur操作完成后进行替换
public static void reverseLinkedList(Node head) {
if (head.next == null || head.next.next == null) {
return
}
Node cur = head.next
//指向当前节点的下一个节点
Node next = null
Node reverseHead = new Node(0,"")
//遍历原来的列表
while (cur != null) {
next = cur.next
cur.next = reverseHead.next
reverseHead.next = cur
cur = next
}
//将head.next指向reverseHead.next
head.next = reverseHead.next
}
从尾到头打印单链表
public static void reversePrint(Node head) {
if (head.next == null) {
return
}
//创建栈
Stack<Node> nodes = new Stack<>()
Node cur = head.next
while (cur != null) {
nodes.push(cur)
cur = cur.next
}
while (nodes.size() > 0) {
System.out.println(nodes.pop())
}
}
将两个有序单链表合并
- 当存在
curOne.id > curTwo.id,则将curTwo及其以后序的链表全部移动到cur后,cur移动到curwo,curTwo向后移;当存在curOne.id < curTwo.id,同理移动curOne及后面的所有链表。假定curTwo为空,curTwo被循环完毕,则表示curOne后续的元素都比curTwo大,直接接到cur后面加就好
public static Node merge(Node headOne , Node headTwo) {
if (headOne.next == null) {
return headTwo
}
if (headTwo.next == null) {
return headOne
}
Node newHead = new Node(0,"")
Node curOne = headOne.next
Node curTwo = headTwo.next
Node cur = newHead
while (curOne != null && curTwo != null) {
if (curOne.id > curTwo.id) {
cur.next = curTwo
cur = curTwo
curTwo = curTwo.next
} else {
cur.next = curOne
cur = curOne
curOne = curOne.next
}
}
if (curOne == null) {
cur.next = curTwo
} else {
cur.next = curOne
}
return newHead
}
单向环形链表(Josephu约瑟夫问题)
- 问题,设编号为1、2、3....的n个人围坐一圈,约定编号k(1<=k<=n)的人从1开始报数,数到m的那个人出列,m的下一位又开始报数,数到m的人再次出列,以此类推,直到所有人出列为止,由此产生一个出列的序列
- 例如 n=5,k=1,m=2 从1开始数2下出来一个人,顺序为
2 4 1 5 3
构建
class Node {
private int id;
private Node next;
//getter setter 构造 toString...
}
class CircleSingleLinkedList {
private Node first;
}
- 添加,第一个特殊处理,要将
fist和cur赋值,且要自己连自己;最后一个节点的next要指向first
public void addNode(int nums) {
if (nums < 1) {
System.out.println("nums is erroe")
return
}
//辅助变量
Node cur = null
//创建环形链表
for (int i = 1
//根据编号创建
Node node = new Node(i)
if (i == 1) {
first = node
first.setNext(first)
cur = first
} else {
//让cur连接新node
cur.setNext(node)
//让node连回first
node.setNext(first)
//cur后移
cur = node
}
}
}
public void show() {
if (first == null) {
System.out.println("List is empty.");
return;
}
Node cur = first;
while (true) {
System.out.println(cur);
if (cur.getNext() == first) {
break;
}
cur = cur.getNext();
}
约瑟夫问题
- 特例分析,当只有两个节点的时候,假如此时只用两个是
1,3 first指向1,end指向3,数2下,3准备出圈,数好后,first先指向3,end先指向1,输出3后,first重新指向1,而此时end只负责绑定first,所以end == first
/**
* @param startId 第几个开始
* @param countNum 数几下出圈
* @param nums 圈里的node个数
*/
public void countNode(int startId, int countNum, int nums) {
this.addNode(nums)
if (first == null || startId < 1 || startId > nums) {
System.out.println("error")
return
}
//辅助指针,记录最后一个节点,用于出圈后的重新连接
Node end = first
while (true) {
if (end.getNext() == first) {
break
}
end = end.getNext()
}
//移动first和end到开始的位置,first到开始报数的node,end到前一个
//例如 startId = 3 需要移动 startId - 1 = 2次
for (int i = 0
first = first.getNext()
end = end.getNext()
}
//报数
while (true) {
//圈中只有一个node
if (end == first) {
break
}
//自己要报一次
for (int i = 0
first = first.getNext()
end = end.getNext()
}
//first指向要出圈的node
System.out.println(first.getId())
//将end.next连上first.next
first = first.getNext()
end.setNext(first)
}
System.out.println(first.getId())
}