• 双指针


    • 双指针的核心思想是优化时间,将朴素的两重循环O(n ^ 2)算法优化成O(n)
    O(n ^ 2)
    for(int i - 0; i < n; i ++ )
    	for(int j = 0; j < n; j ++ )
    		exec()
    将上面的朴素算法优化成O(n)
    
    • 双指针的通用框架
    for(int i = 0, j = 0; i < n; i ++ ) {
    	while(j < i && check(i, j)) j ++;
    	//每道题目的具体逻辑
    }
    
    • 一个简单的例子:输入一个字符串,将其中的每个单词单独输出
    int main() {
    	char str[1000];
    	gets(str);
    	int n = strlen(n);
    	for(int i = 0; i < n; i ++ ) {
    	    int j = i;
    	    while(j < n && str[i] != ' ') j ++ ;
    	
    	    for(int k = i; k < j; k ++ ) cout << str[k];
    	    cout << endl;
    	    i = j;
    	}
    	
    	return 0;
    }
    
    • 单调性

    最长连续不重复子序列


    双指针

    1.快慢指针

    快慢指针一般都初始化指向链表的头节点head,前进时快指针fast在前,慢指针slow在后

    1.1 判断链表中是否含有环

    解法思路:两个指针,一个每次前进两步,一个每次前进一步。如果不含有环,跑得快的那个指针最终会遇到Null;如果含有环,快指针最终会超慢指针一圈,和慢指针相遇,说明链表含有环。

    boolean hasCycle(ListNode head){
    	ListNode fast,slow;
    	fast = slow = head;
    	while (fast != null && fast.next != null){
    		fast = fats.next.next;
    		slow = slow.next;
    	
    		if (fast == slow) return true;
    	}
    	return false;
    }
    

    1.2 已经知道链表中含有环,返回环的位置

    ListNode detectCycle(ListNode,head){
    	ListNode 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;
    }
    

    1.3 寻找链表的中点

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

    如果链表的长度时奇数,slow恰好停留在中点位置,如果为偶数,slow最终的位置是中点偏右

    1.4 寻找链表中倒数第k个元素

    ListNode slow,fast;
    slow = fast = head;
    while( k-- > 0) fast = fast.next;//让快指针先走k步,之后快慢指针同步推进
    while(fast != null){
    	slow = slow.next;
    	fast = fast.next;
    }
    return slow;
    

    1.5 第26题:删除排序数组中的重复项

    class Solution{
    public:
        int removeDuplicates(vector<int>& nums){
            if(nums.size() == 0){
                return 0;
            }
            int slow = 0, fast = 0;
            while(fast < nums.size()){
                if(nums[fast] != nums[slow]){
                    slow++;
                    nums[slow] = nums[fast];
                }
                fast++;
            }
            return slow+1;
        }
    };
    

    2.左右指针

    左右指针在数组中实际是指两个索引值,一般初始化为left = 0, right = length -1

    2.1 二分查找

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

    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};
    }
    

    2.3 反转数组

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

    3.二分查找

    参考文章

    • 二分查找的思路很明确,难点在于处理边界条件,在处理时要关注“循环不变量”,并在整个程序过程中一以贯之,具体来讲:

    • 1)采用“左闭右闭”:

      int left = 0,right = length - 1;
      while(left <= right){
          mid = left + (right - left)/2;
          ...
          left = mid + 1;
          ...
          right = mid - 1;
      }
      
    • 2)采用“左闭右开”:

      int left = 0, right = length;
      while(left < right){
          mid = left + (right - left)/2;
          ....
          left = mid + 1;
          ....
          right = mid;
      }
      

    3.1 二分查找框架

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

    3.2 寻找一个数

    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;
    }
    

    思考:

    1、为什么 while 循环的条件中是 <=,而不是 <?

    2、为什么left = mid + 1right = mid - 1?我看有的代码是right = mid或者left = mid,没有这些加加减减,到底怎么回事,怎么判断?

    3.3 寻找左侧边界的二分查找

    int left_bound(int[] nums, int target) {
        if (nums.length == 0) return -1;
        int left = 0;
        int right = nums.length; // 注意
    
        while (left < right) { // 注意
            int mid = (left + right) / 2;
            if (nums[mid] == target) {
                right = mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid; // 注意
            }
        }
        return left;
    }
    
    为什么该算法能够搜索左侧边界**?

    答:关键在于对于nums[mid] == target这种情况的处理:

        if (nums[mid] == target)
            right = mid;
    

    可见,找到 target 时不要立即返回,而是缩小「搜索区间」的上界right,在区间[left, mid)中继续搜索,即不断向左收缩,达到锁定左侧边界的目的。

    3.4 寻找右侧边界的二分查找

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

    3.5 逻辑统一

    对于寻找左右边界的二分搜索,常见的手法是使用左闭右开的「搜索区间」,根据逻辑将「搜索区间」全都统一成了两端都闭,便于记忆,只要修改两处即可变化出三种写法

    int binary_search(int[] nums, int target) {
        int left = 0, right = nums.length - 1; 
        while(left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1; 
            } else if(nums[mid] == target) {
                // 直接返回
                return mid;
            }
        }
        // 直接返回
        return -1;
    }
    
    int left_bound(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] == target) {
                // 别返回,锁定左侧边界
                right = mid - 1;
            }
        }
        // 最后要检查 left 越界的情况
        if (left >= nums.length || nums[left] != target)
            return -1;
        return left;
    }
    
    
    int right_bound(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] == target) {
                // 别返回,锁定右侧边界
                left = mid + 1;
            }
        }
        // 最后要检查 right 越界的情况
        if (right < 0 || nums[right] != target)
            return -1;
        return right;
    }
    
  • 相关阅读:
    MySQL crash-safe replication
    Innodb单表数据物理恢复
    从MySQL全库备份中恢复一个库和一张表
    mysqldump中master-data和dump-slave的区别
    mysqldump常用参数
    mysqldump --single-transaction 和--lock-tables参数详解
    CF757E Bash Plays with Functions
    通过binlog日志统计dml语句,找出操作频繁的表
    [SDOI2015]约数个数和
    常用binlog日志操作命令
  • 原文地址:https://www.cnblogs.com/huhu555/p/14650892.html
Copyright © 2020-2023  润新知