ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

5.双指针技巧汇总

2022-05-17 21:33:59  阅读:164  来源: 互联网

标签:slow 技巧 int 汇总 fast next 链表 指针


双指针技巧汇总

快慢指针:主要解决链表中的问题,比如典型的判定链表中是否包含环

左右指针:主要解决数组(或字符串)中的问题,比如二分查找

快慢指针

快慢指针一般都初始化指向链表的头结点 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、已知链表中含有环,返回这个环的起始位置

image-20220509231838935

当快慢指针相遇时,让其中任一个指针指向头结点,然后让它俩以相同的速度前进,再次相遇时所在的节点位置就是环开始的位置

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步一定就是走到环起点了

image-20220509231847392

3、寻找链表的中点

快指针一次前进两步,慢指针一次前进一步,当快指针到达链表尽头时,慢指针就处于链表做的中间位置。

当链表的长度是奇数时,slow恰巧停在中点位置;如果长度是偶数,slow 最终的位置是中间偏右

image-20220509231855130

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)

标签:slow,技巧,int,汇总,fast,next,链表,指针
来源: https://www.cnblogs.com/autumnmoonming/p/16282437.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有