链表问题
1.快慢指针法:
快慢指针一般都初始化指向链表的头结点head,前进时fast在前,慢指针slow在后。fast每次前进2步,slow每次前进一步
1.判定链表中是否含有环
单链表的特点是:
只知道每个结点的下一个结点,所以一个指针的话无法判断链表中是否有环。如果链表中不含环,那么指针最终会遇到空指针null表示链表到头。
但是如果链表有环,那么指针就会陷入死循环,因为环形数组中没有null指针作为尾部节点。
最终fast指针会超slow指针一圈,和slow指针相遇,说明有环
public class Solution{
public boolean hasCycle(ListNode head){
ListNode fast,slow;
fast=slow=head;
while(fast!=null&&fast.next!=null){
fast = fast.next.next;
slow = slow.next;
if(fast==slow) return true;
}
return false;
}
}
2.已知链表有环,返回这个环的起始位置
讲解:https://www.freesion.com/article/4203888940/
当快慢指针相遇时,让其中任一个指针重新指向头节点,然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置。
第一次相遇时,假设慢指针 slow 走了 k 步,那么快指针 fast 一定走了 2k 步,也就是说比 slow 多走了 k 步(也就是环的长度)
设相遇点距环的起点的距离为 m,那么环的起点距头结点 head 的距离为 k - m,也就是说如果从 head 前进 k - m 步就能到达环起点,巧的是,如果从相遇点继续前进 k - m 步,也恰好到达环起点。
所以,只要我们把快慢指针中的任一个重新指向 head,然后两个指针同速前进,k - m 步后就会相遇,相遇之处就是环的起点了。
public class solution{
public ListNode detectCycle(ListNode head){
ListNode fast , slow;
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;
//是因为可能存在链表中只有一个值,那么相当于根本没有进行第一个循环
//这行就是为了处理这个特殊的可能性
//即只有一个节点
if(fast==null||fast.next==null) return null;
fast=head;
while(fast!=slow){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
3.寻找链表的中点(寻找链表中点的一个重要作用是对链表进行归并排序)
我们还可以让快指针一次前进两步,慢指针一次前进一步,当快指针到达链表尽头时,慢指针就处于链表的中间位置。
当链表的长度是奇数时,slow 恰巧停在中点位置;如果长度是偶数,slow 最终的位置是中间偏右:
class solution{
public ListNode middleNode(ListNode head){
ListNode fast,slow;
fast = slow = head;
while(slow!=null){
if(fast.next == null){
return slow;
}
if(fast.next.next == null){
return slow.next;
}
slow = slow.next;
fast = fast.next.next;
}
return null;
}
}
4.寻找链表倒数第k个元素
思路还是使用快慢指针,让快指针先走 k 步,然后快慢指针开始同速前进。这样当快指针走到链表末尾 null 时,慢指针所在的位置就是倒数第 k 个链表节点(为了简化,假设 k 不会超过链表长度)
class Solution{
public ListNode getKthFromEnd(ListNode head,int k){
ListNode fast,slow;
fast=slow=head;
for(int i=0;i<k;i++){
fast = fast.next;
}
while(fast!=null){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
2.链表反转
1.递归法链表反转
链表反转解释:
https://www.cnblogs.com/keeya/p/9218352.html
https://blog.csdn.net/javageektech/article/details/105236461
时间复杂度:O(n)
空间复杂度:O(n)
public Node recursiveList(Node node){
if(node==null||node.next==null){
return node;
}
Node newNode =recursiveList(node.next);
//将下一个结点的指针指向当前指针
node.next.next = node;
//切断当前指针与下一个指针的连接
node.next = null;
return newNode;
}
2.遍历法链表反转
时间复杂度:O(n)
空间复杂度:O(1)
public static Node reverse(Node list){
Node current=list, // 定义 current 为当前链表
afterReverse=null; // 定义 afterReverse 为转换之后的新链表,初始为 null
// 当前链表不为空,进行反转操作
while (current!=null){
// 1. 保存当前节点的 next 指针指向的链表
Node next=current.next;
// 2. 将当前节点的 next 指针指向反转之后的新链表
current.next=afterReverse;
// 3. 保存当前的链表状态到新链表中
afterReverse=current;
// 4. 将当前节点指针后移一位,进行下一次循环
current=next;
}
return afterReverse;
}
3.前N个链表反转
/**
*反转单链表前 n 个节点
* @param list 为传入的单链表 , n 为要反转的前 n 个节点
*/
public static Node next;
public static Node reverseListN(Node list,int n){
if (n == 1) {
// 要进行反转链表时,先将 list 后的节点数据保存到 next 中
next = list.next;
return list;
}
Node reverse = reverseListN(list.next , n-1);
list.next.next = list;
// 将 list.next 的指针指向没有进行反转的链表
list.next = next ;
return reverse;
}
4.反转一部分链表
/**
*反转部分单链表
* @param list 为传入的单链表, m 为开始反转的节点, n 为结束的反转节点
*/
public static Node reverseBetween(Node list , int m , int n){
if (m == 1){
return reverseListN(list,n);
}
list.next = reverseBetween(list.next,m-1,n-1);
return list;
3. 链表的插入
讲解:https://blog.csdn.net/unwrapping/article/details/108578202
1.头插法链表
private ListNode head = new ListNode(0, "", "");
//定义一个头结点,头结点不存放数据,只有下一个结点才会存放数据
public void headinsertlist(ListNode node){
if(head.next==null){
//判断是否只有一个头结点第一次插入值
head.next = node;
return;
}
else{
//如果不是第一次插入值
ListNode temp = head.next;
//将head的下一个结点用temp引用
head.next = node;
//将head.next的节点指向新插入节点node,断掉head指向temp节点的指针
node.next = temp;
//将新节点的node.next指针指向temp
}
}
2.尾插法链表
public void tailinsertlist(ListNode node){
if(head.next == null){
head.next = node;
return;
}
else{
ListNode temp = head.next;
while(true){
if(temp.next==null){
break;
}
else temp=temp.next;
}
temp.next = node;
}
}
代码示例:
import java.util.List;
public class text4 {
class ListNode{
int data;
ListNode next=null;
ListNode(int data){
this.data = data;
}
}
int total=0;
//链表里节点个数
ListNode head= new ListNode(0);
public boolean HasCycle(ListNode head){
ListNode fast,slow;
fast=slow=head;
while(fast!=null||fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
return true;
}
}
return false;
}
public ListNode DetectCycleReturnZero(){
ListNode fast,slow;
fast=slow=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 slow;
}
public void DetectListMiddle(){
ListNode fast,slow;
fast=slow=head;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow = slow.next;
}
System.out.println("中点位置: "+IndexCount(slow));
}
public void DeleteList(int num){
if(head.next==null){
System.out.println("链表无数据");
}
else{
ListNode pre = Index((num-1));
ListNode cur = Index(num);
if(pre==null&&cur==null){
System.out.println("没有该节点");
}
else if(cur!=null&&cur.next==null){
pre.next=null;
}
else {
pre.next=cur.next;
cur.next = null;
}
}
total--;
}
public void UpdataList(int num,int data){
if(head.next==null){
System.out.println("链表无数据");
}
else {
ListNode tmp = Index(num);
tmp.data = data;
}
}
public ListNode Index(int num){
ListNode tmp=null;
if(total<num||num<0)return null;
if(head.next==null){
System.out.println("链表无数据");
}
else{
tmp = head.next;
int count = 1;
while(count!=num){
tmp=tmp.next;
count++;
}
}
return tmp;
}
public int IndexCount(ListNode node){
if(node==null)return -1;
ListNode tmp=null;
int count = 1;
if(head.next==null){
System.out.println("链表无数据");
}
else{
tmp = head.next;
while(tmp!=node){
tmp=tmp.next;
count++;
}
}
return count;
}
public ListNode ReverseListRecursive(ListNode node){
//空间效率低于遍历法
if(node==null||node.next==null){
head.next=node;
return node;
}
ListNode tmp = ReverseListRecursive(node.next);
node.next.next=node;
node.next=null;
return tmp;
}
public void ReverseListTraverse(){
ListNode cur = head.next;
ListNode pre = null;
while(cur!=null){
ListNode next = cur.next;
cur.next = pre;
pre=cur;
cur=next;
}
head.next=pre;
}
public void HeadAddList(ListNode node){
if(head.next==null){
head.next = node;
}
else {
ListNode temp = head.next;
head.next = node;
node.next = temp;
}
total++;
}
public void TailAddList(ListNode node){
if(head.next==null){
head.next = node;
}
else{
ListNode tmp = head.next;
while (tmp.next != null) {
tmp = tmp.next;
}
tmp.next = node;
}
total++;
}
public ListNode DetectBackwardList(int num){
ListNode fast,slow;
fast=slow=head;
if(total<num||num<0) return null;
for (int i = 0; i < num; i++) {
fast=fast.next;
}
while(fast!=null){
fast=fast.next;
slow=slow.next;
}
return slow;
}
ListNode Rnext=null;
//记录最后一个节点的下一个节点,用于,第一个节点指向它,保持连续,需要设置成全局变量
public ListNode ReverseListFromZeroToNum(ListNode node,int num){
if(num==1){
Rnext = node.next;
head.next = node;
return node;
}
ListNode tmp = ReverseListFromZeroToNum(node.next,num-1);
node.next.next=node;
node.next=Rnext;
return tmp;
}
public ListNode ReserveListBetween(ListNode node,int m,int n){
if(m==1){
return ReverseListFromZeroToNum(node,n);
}
node.next = ReserveListBetween(node.next,m-1,n-1);
head.next = node;
return node;
}
public ListNode ProduceNode(int data){
return new ListNode(data);
}
public void PrintList(){
if(head.next==null){
System.out.println("kong");
}
else{
ListNode tmp = head.next;
int count = 1;
while(tmp!=null){
System.out.println("节点: "+count+++" "+tmp.data);
tmp=tmp.next;
}
}
}
public static void main(String[] args) {
text4 t = new text4();
t.HeadAddList(t.ProduceNode(4));
t.HeadAddList(t.ProduceNode(45));
t.HeadAddList(t.ProduceNode(499));
t.TailAddList(t.ProduceNode(889));
t.TailAddList(t.ProduceNode(524));
t.TailAddList(t.ProduceNode(257));
t.PrintList();
t.DeleteList(3);
t.PrintList();
t.UpdataList(2,4444);
t.PrintList();
t.DetectListMiddle();
System.out.println(t.IndexCount(t.DetectBackwardList(15)));
t.ReverseListRecursive(t.head.next);
t.PrintList();
System.out.println("/");
t.ReverseListTraverse();
t.PrintList();
System.out.println("**********");
t.ReverseListFromZeroToNum(t.head.next,3);
t.PrintList();
System.out.println("-----------");
t.ReserveListBetween(t.head.next,2,4);
t.PrintList();
}
}