1. 程式人生 > 其它 >連結串列中的雙指標總結

連結串列中的雙指標總結

技術標籤:資料結構與演算法連結串列指標leetcode演算法

連結串列中的雙指標問題:

// 初始化快慢指標
ListNode slow = head;
ListNode fast = head;
/**
* 對於每個問題具體改變
* 注意避免空指標異常
**/
while (slow != null && fast != null && fast.next != null) {
slow = slow.next; // 每次移動慢指標一步
fast = fast.next.next; // 每次移動快指標兩步
if (slow == fast) { // 具體問題具體修改

return true;
}
}
return false;

需要把雙指標技巧分為兩類,一類是快慢指標,一類是左右指標。前者主要解決連結串列中的問題,比如典型的判別連結串列中是否包含環;後者主要解決陣列(或者字串)中的問題,比如二分查詢。

一、快慢指標的常見演算法

快慢指標一般都初始化指向連結串列的頭結點head,前進時快指標fast在前,慢指標在後。

1.判斷連結串列中是否有環

單鏈表的特點是每個節點只知道下一個節點,所以一個指標的話無法判斷連結串列是否含有環。如果連結串列中有環,那麼這個指標最終會遇到null.

boolean hasCycle(ListNode head){
    while(head != null){
        head = head.next;
    }
    return false;
}

如果有環,那麼這個指標就會陷入死迴圈。

用快慢指標,如果不含有環,跑得快的那個指標最終會遇到null,說明連結串列中不含有環;如果含有環,快指標最終會與慢指標相遇,說明有環。類似小學數學中環形操場賽跑的追擊問題。

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.已知連結串列中含有環,返回這個換的起始位置

參見這篇部落格

3.尋找連結串列的中點

讓快指標一次前進2步,慢指標一次前進1步,當快指標到達連結串列盡頭時,慢指標就處於連結串列的中間位置。

while(null != fast && null != fast.next){
    fast = fast.next.next;
    slow = slow.next;
}

// slow 就在中間位置
return slow;

如果連結串列長度是奇數,slow在中點位置;如果是偶數,slow在中間偏右;

4.尋找連結串列的倒數第K個元素【刪除連結串列的第k個元素】

先讓快指標走k步,然後快慢指標開始同速度前進。這樣當快指標走到連結串列末尾null時,慢指標所在的位置就是倒數第k個連結串列節點【但是要注意特殊情況--只有一個節點、刪除的是頭結點】

ListNode slow, fast;
slow = fast = head;
while(k-- > 0){ // 讓快指標先移動k步
    fast = fast.next;
}
while(fast != null){ // 此時兩者的間隔剛剛是K,當fast到達結尾時,slow剛剛是倒數第k個
    slow = slow.next;
    fast = fast.next;
}
return slow;

二、左右指標的常用演算法

左右指標在陣列中實際是兩個索引值,一般初始化為left = 0,right=nums.length-1.

1.二分查詢

int binarySearch(int[] nums, int target){
    int left = 0;
    int right = nums.length - 1;
    while(left <= right){
        if (nums[mid] == target){
            return mid;
        } else if (nums[mid] < target){
            left = mid + 1;
        }else if (nums[mid] > target){
            right = mid - 1;
        }
    }
    return -1;
}
        

2.兩數之和【leetcode第一道題】

只要陣列有序,就應該想到雙指標技巧。

int[] twoSum(int[] nums, int target){
    int left = 0, right = nums.length - 1;
    while(left < right){
        int sum = nums[left] + nums[right];
        if (sum == target){
            return new int[]{left + 1, right + 1};
        } else if (sum < target){
            left++;
        } else if (sum > target){    
            right--;
        }
    }
    return new int[]{-1, -1};
}

3.反轉陣列

void reverse(int[] nums){
    int left = 0;
    int right = nums.length - 1;
    while(left < right){
        // swap(a,b);
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
        left++; right--;
    }
}