单链表问题
1.单链表及节点的定义
class Node{
public int data;
public Node next;
public Node(){
}
public Node(int data) {
this.data = data;
}
}
class LinkedList {
public Node head;
//在此实现单链表的插入,删除等方法
}
2.单链表的头插法
//头插法
public void addFirst(int data) {
Node node = new Node(data);
//第一次插入,单链表为空
if (this.head == null) {
this.head = node;
} else {
//单链表不为空
node.next = this.head;
this.head = node;
}
}
3.单链表的尾插法
//尾插法
public void addLast(int data) {
Node node = new Node(data);
if (this.head == null) {
this.head = node;
} else {
Node cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
}
4. 单链表-从指定位置插入
这里用到了几个方法,获取单链表长度,下标的合法性检查,找index-1位置节点。
//从指定位置插入
public boolean addIndex(int index, int data) {
//1.先检查下标的合法性
checkIndex(index);
// 若下表为0,则采用头插法
if (index == 0) {
addFirst(data);
}
//找到index-1 位置的节点,作为要插入位置的前趋
Node cur = searchIndex(index);
Node node = new Node(data);
node.next = cur.next;
cur.next = node;
return true;
}
//检查下表合法性
private void checkIndex(int index) {
if (index < 0 || index > getLength()) {
throw new IndexOutOfBoundsException("下标不合法");
}
}
//获取链表长度
private int getLength() {
int count = 0;
Node cur = this.head;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
//寻找index-1位置的节点
private Node searchIndex(int index) {
int count = 0;
Node cur = this.head;
while (count < index - 1) {
cur = cur.next;
count++;
}
return cur;
}
5.删除第一次出现关键字为key的节点
单链表的节点删除,需要找到前趋节点进行绑定,在此方法中实现了寻找前趋节点的方法。
//删除第一次出现关键字为key的节点
public void remove(int key) {
if (this.head.data == key) {
this.head = this.head.next;
return;
}
Node prev = searchPrev(key);
if (prev == null) {
return;
}
Node del = prev.next;
prev.next = del.next;
}
//寻找前趋节点
private Node searchPrev(int key) {
Node prev = this.head;
while (prev.next != null) {
if (prev.next.data == key) {
return prev;
}
prev = prev.next;
}
return null;
}
6.删除所有关键字为key的节点
遍历单链表,把所有值为key 的节点删除
//删除所有关键字为key的节点
public void removeAllKey(int key) {
if (this.head.data == key) {
this.head = this.head.next;
}
Node prev = this.head;
Node cur = this.head.next;
while (cur != null) {
if (prev.next.data == key) {
prev.next = cur.next;
cur = cur.next;
} else {
prev = cur;
cur = cur.next;
}
}
}
7.单链表的打印
//打印链表
public void display() {
Node cur = this.head;
while (cur != null) {
System.out.print(cur.data + " ");
cur = cur.next;
}
System.out.println();
}
8.单链表的清空
//清空单链表
public void clear() {
this.head = null;
}
9.单链表的逆置/反转
//单链表的逆置,反转
public Node reverseList() {
Node prev = null;//当前需反转节点的前趋
Node cur = this.head;//当前需反转的节点
Node newHead = null;// 新的单链表的头结点
while (cur != null) {
Node curNext = cur.next;
if (curNext == null) {
newHead = cur;
}
cur.next = prev;
prev = cur;
cur = curNext;
}
return newHead;
}
//反转完之后需重写打印函数
public void display2(Node newHead) {
Node cur = newHead;
while (cur != null) {
System.out.print(cur.data + " ");
cur = cur.next;
}
System.out.println();
}
10.单链表-找中间节点
返回中间节点;
定义快指针fast,慢指针slow,fast走两步,slow走一步
当fast为null,或者fast的next为null时,slow所指刚好为中间节点
public Node middleNode() {
Node fast = this.head;
Node slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
11.单链表-找倒数第K个节点
找倒数第k个节点:
先让fast走k-1步,
再让fast、slow一起走,
当fast下一个无节点时,slow刚好指向倒数第k个节点
public Node findKthToTail(int k) {
Node fast = this.head;
Node slow = this.head;
while (k - 1 > 0) {
if (fast.next != null) {
fast = fast.next;
k--;
} else {
System.out.println("没有这个节点!");
return null;
}
}
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
12.以基准重新排列链表
以基准重新排列链表
方法:基准前的比基准小->产生一个新的顺序表,基准后的比基准大->产生一个顺序表;
将两个顺序表拼接起来.
public Node partition(int x) {
Node beforeStart = null;
Node beforeEnd = null;
Node afterStart = null;
Node afterEnd = null;
Node cur = this.head;
while (cur != null) {
Node curNext = cur.next;
cur.next = null;
if (cur.data < x) {
if (beforeStart == null) {
beforeStart = cur;
beforeEnd = cur;
} else {
beforeEnd.next = cur;
beforeEnd = cur;
}
} else {
if (afterStart == null) {
afterStart = cur;
afterEnd = cur;
} else {
afterEnd.next = cur;
afterEnd = cur;
}
}
cur = curNext;
}
//如果第一个部分没有数据,也就是没有比基准小的数据
if (beforeStart == null) {
return afterStart;
}
beforeEnd.next = afterStart;
return beforeStart;
}
13.删除单链表中重复节点
//删除重复节点
public Node deleteDuplication() {
Node newHead = new Node(-1);
Node tmp = newHead;
Node cur = this.head;
while (cur != null) {
//找到相同的节点
if (cur.next != null && cur.data == cur.next.data) {
while (cur.next != null && cur.data == cur.next.data) {
cur = cur.next;
}
cur = cur.next;
tmp.next = cur;
} else {
//没找到相同的节点
tmp.next = cur;
tmp = tmp.next;
cur = cur.next;
}
}
return newHead.next;
}
14.单链表回文结构判断
回文结构:
定义快慢指针,让fast走两步,slow走一步,fast为空或者fast的下一个节点为空时,slow刚好在中间位置;
反转后半部分,从中间的下一个反转,1->2->3->2->1;
定义p指向中间的下一个,完成反转 1->2->3<-2<-1;
遍历单链表,head 从前面走,slow从尾巴走,直到相遇前对应位置元素均相等,则是回文结构。
public boolean clkPalindrome() {
//单链表为空,不是回文结构
if (this.head == null) {
return false;
}
//单链表只有一个节点,是回文结构
if (this.head.next == null) {
return true;
}
Node fast = this.head;
Node slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
Node p = slow.next;
while (p != null) {
Node pNext = p.next;
p.next = slow;
slow = p;
p = pNext;
if (p != null) {
pNext = p.next;
}
}
while (head != slow) {
if (this.head.next == slow) {
return true;
}
if (head.data != slow.data) {
return false;
}
head = head.next;
slow = slow.next;
}
return true;
}
15.判断单链表是否有环
我们可以自己创造一个环,来判断方法的正确性
//创造一个环
public void createCycle() {
Node cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = this.head.next.next;
}
//判断链表是否有环
//一个指针走一步,一个指针走两步,若有环,能在最短时间内尽早相遇
//若一个走三步,一个走两步,有可能会跳过,很久才会相遇或者永远不会相遇。
public boolean hasCycle() {
Node fast = this.head;
Node slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
return true;
}
}
return false;
}
16.找到环的入口节点
找到环的入口节点:
从头开始走,fast 走两步,slow 走一步,找到第一次相遇的点;
第一次相遇后,将一个拉到单链表的头,这里将fast 拉到头,slow不动;
让fast、slow都一步一步走,再次相遇就为环的入口点。
public Node detectCycle(Node node) {
Node fast = this.head;
Node slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
break;
}
}
if (fast == null || fast.next == null) {
return null;
}
fast = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
}