注:题目来自于《程序员代码面试指南:IT名企算法与数据结构题目最优解》,该书是左程云老师的著作,值得推荐,这里仅是记录一下该书中题目的解法和个人理解
题目一:在单链表和双链表中删除倒数第K个节点
描述:
分别实现两个函数,一个可以删除单链表中倒数第K个节点,另一个可以删除双链表中倒数第K个节点
思路:
这里书中给出的方法是设定了一个变量K,用K来判断指针位置,但是我比较喜欢用快慢指针法去解决此类问题,这里给出一种使用快慢指针发的解法。
单项链表首先设定一个快指针,sign,比head指针先移动k+1个位置,保证在sign走到链表尾节点的时候,head可以指向目标位置前一个节点,然后只需要令head.next = head.next.next即可。
双向链表更为简单,可以令sign比head指针先移动K个位置,这样在sign指向尾节点的时候,head正好指向要删除的位置,此时只需要将head.last.next = head.next; head.last.last = head.last即可。
只介绍单项链表的写法
链表代码:
public class Node {
public int value;
public Node next;
public Node(int data){
this.value = data;
}
}
删除方法及测试类:
public class RemoveElement {
public static void main(String[] args) {
// init
Node head = new Node(1);
Node node1 = new Node(2);
Node node2 = new Node(3);
Node node3 = new Node(4);
head.next = node1;
node1.next = node2;
node2.next = node3;
// test
System.out.println("删除节点之前:");
Node h = head;
while (h != null) {
System.out.print(h.value + " ");
h = h.next;
}
System.out.println();
int k = 2;
removeLastKthNode(head, k);
System.out.println("删除节点之后:");
while (head != null) {
System.out.print(head.value + " ");
head = head.next;
}
}
private static void removeLastKthNode(Node head, int k) {
if (k < 0) {
throw new RuntimeException("参数有误");
}
Node sign = head;
while (k-- >= 0) {
sign = sign.next;
if (sign == null) {
throw new RuntimeException("参数有误");
}
}
while (sign != null) {
sign = sign.next;
head = head.next;
}
head.next = head.next.next;
}
}
测试结果,亲测可用:
删除节点之前:
1 2 3 4
删除节点之后:
1 2 4
题目二:删除链表a/b处的节点
问题描述:
给定链表头节点head,实现删除链表中间节点的函数
例如:
链表1->2->3->4->5 假设a/b的值为r
如果r等于0,不删除任何节点
如果r在区间(0,1/5】上,删除节点1
如果r在区间(1/5,2/5】上,删除节点2
…
如果r大于1,不删除任何节点
思路:
问题仅在于确定第几个节点为删除节点,找到该节点的前一节点即可。
图解:
分析起来并不困难,可以取一个特殊值,以特殊代一般,总结规律,比如链表 长度7 a= 5 b = 6
5/67=5.83333向上取整为6
所在区间
5/6=0.8333
5/7<5/6<=6/7,删除节点6
结论:
a/6链表长度向上取整,既为要删除的节点
链表代码:
public class Node {
public int value;
public Node next;
public Node(int data){
this.value = data;
}
}
方法及测试代码:
public class RemoveElementTwo {
public static void main(String[] args) {
// init
Node head = new Node(1);
Node node1 = new Node(2);
Node node2 = new Node(3);
Node node3 = new Node(4);
Node node4 = new Node(5);
Node node5 = new Node(6);
Node node6 = new Node(7);
head.next = node1;
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node6;
int a = 5;
int b = 6;
// test
System.out.println("删除节点之前:");
Node h = head;
while (h != null) {
System.out.print(h.value + " ");
h = h.next;
}
System.out.println();
deleteElement(head, a, b);
System.out.println("删除节点之后:");
while (head != null) {
System.out.print(head.value + " ");
head = head.next;
}
}
private static void deleteElement(Node head, int a, int b) {
Node h = head;
int len = 0;
while (h != null) {
h = h.next;
len++;
}
//要删除第几个节点
int num = (int) Math.ceil(a * len / (double) b);
//找到前一个节点,本身循环就从1节点开始,同时选择删除前一节点 所以2 = 0 + 1+ 1
for(int i = num;i-2>0;i--){
head = head.next;
}
head.next = head.next.next;
}
}
执行结果,亲测可用:
删除节点之前:
1 2 3 4 5 6 7
删除节点之后:
1 2 3 4 5 7