1. 程式人生 > 其它 >5.雙指標技巧彙總

5.雙指標技巧彙總

雙指標技巧彙總

快慢指標:主要解決連結串列中的問題,比如典型的判定連結串列中是否包含環

左右指標:主要解決陣列(或字串)中的問題,比如二分查詢

快慢指標

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

1、判定連結串列中是否包含環

單鏈表的特點就是每個節點只知道下一個節點,所以一個指標的話無法判斷連結串列中是否含有環。

雙指標,如果不含環,跑的快的最終會遇到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、已知連結串列中含有環,返回這個環的起始位置

當快慢指標相遇時,讓其中任一個指標指向頭結點,然後讓它倆以相同的速度前進,再次相遇時所在的節點位置就是環開始的位置

ListNode detectCycle(ListNode head){
	LsitNode fast, slow;
	fast = slow = head;
	while(fast != null && fast.next != null){
		fast = fast.next.next;
		slow = slow.next;
		if(fast == slow) break;
	}
	slow = head;
	while(slow != fast){
		fast = fast.next;
		slow = slow.next;
	}
	return slow
}

第一次相遇時,慢指標 slow 走了 k 步,那麼快指標 fast 一定走了 2k 步

多走的 k 就是 fast 指標在環裡轉圈圈,k 是環長度的【整數倍】

設 相遇點距環的起點 距離為 m,那環的起點距頭結點 head 的距離 為 k - m

也就是說,從head 前進 k-m步就能到達環起點

巧的是,如果從相遇點繼續前進 k-m步,也恰好到達環起點。不管fast在環裡到底轉了幾圈,反正走 k步可以到相遇點,走k-m步一定就是走到環起點了

3、尋找連結串列的中點

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

當連結串列的長度是奇數時,slow恰巧停在中點位置;如果長度是偶數,slow 最終的位置是中間偏右

ListNode middleNode(ListNode head){
	ListNode fast,slow;
	fast = slow = head;
	while(fast-1=null&&fast.next!=null){
		fast = fast.next.next;
		slow = slow.next;
	}
	return slow;
}

尋找連結串列中點的一個重要作用,是對連結進行歸併排序

陣列的歸併排序:求中點索引遞迴地把陣列二分,最後合併兩個有序陣列。對於連結串列,合併兩個有序連結串列很簡單,難點在於二分

4、尋找連結串列的倒數第 n 個元素

快指標先走n步,然後快慢指標同速前進,這樣當快指標走到連結串列末尾null時,慢指標所在的位置就是倒數第 n 個連結串列節點(n 不會超過連結串列長度)

public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode fast,slow;
        fast =slow = head;
        while(n-- > 0){//快指標先前進n步
            fast = fast.next;
        } 
        if(fast == null){//如果快指標走到頭了
            return head.next;//說明倒數第n個節點就是第一個節點
        }
        while(fast!=null && fast.next != null){
						//同步向前,快指標在前,與慢指標始終相差n
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;//slow.next 就是倒數第 n 個節點,刪除它
        return head;
    }

左右指標

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

1、二分查詢

int binarySearch(int[] nums,int target){
	int left = 0;
	int right = nums.length -1;
	while(left<=right){
		int mid = left + (right - left) / 2;
		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、兩數之和

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

public 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++;//讓sum大一點
            }else if(sum > target){
                right--;
            }
        }
        return new int[]{-1,-1};
    }

3、反轉陣列

public void reverseString(char[] s) {
        int left = 0;
        int right = s.length-1;
        while(left < right){
            char temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }
    }

田忌賽馬?

策略:將齊王和田忌的馬按照戰鬥力排序,然後按照排名一一對比,如果田忌的馬能贏,那就比賽,如果贏不了,那就換個墊底的來換人頭,儲存例項

int n = nums1.length;
sort(nums1);//田忌的馬
sort(nums2);//齊王的馬
for(int i = n-1; i >=0; i--){
	if(nums1[i] > nums2[i]){
		//比得過,跟他比
	}else{
		//比不過,換個墊底的來換人頭
	}
}

需要對兩個陣列排序,nums2中的元素順序不能改變,利用其他資料結構來輔助

雙指標,來處理 【送人頭】的情況

public int[] advantageCount(int[] nums1, int[] nums2) {
        int n =nums1.length;
				//給nums2 降序排序
        PriorityQueue<int[]> maxpq = new PriorityQueue<>(
            (int[] pair1, int[] pair2) -> {
                return pair2[1] - pair1[1];
            }
        );
        for(int i = 0; i<n; i++){
            maxpq.offer(new int[]{i,nums2[i]});
        }    
				//給 nums1 升序排序
        Arrays.sort(nums1);
				//nums1[left] 是最小值,nums1[right] 是最大值
        int left = 0,right = n -1 ;
        int[] res = new int[n];
        while(!maxpq.isEmpty()){
            int[] pair = maxpq.poll();
						// maxval 是nums2 中的最大值, i 是對應索引
            int i = pair[0],maxval = pair[1];
            if(maxval < nums1[right]){
								//如果 nums1[right] 能勝過 maxval ,那就自己上
                res[i] = nums1[right];
                right--;
            }else{
								//否則用最小值混一下,養精蓄銳
                res[i] = nums1[left];
                left++;
            }
        }
        return res;
    }

二叉堆和排序的複雜度O(nlogn)