leedcode 算法 链接
day 1 1. 二分查找 描述 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
分析 二分查找即可
学习算法要形成自己固定的模板。二分查找的模板如下:
统一标准:[low,high) 前闭后开
mid=low+(high-low)/2;
while(low < high)
所以模板如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 low=low; high=high; while (low<high){ mid=low+(high-low)/2 ; if (mid比目标大) high=mid; else if (mid比目标小) low=mid+1 ; else 查找成功,返回。 } return -1 ;
有一点需要注意:若判断mid是否满足条件的时候,当mid满足条件则舍去mid时使用开区间,当mid满足条件而保留mid时使用闭区间。注意while中low和high的比较条件,当low和high满足什么条件时构成的区间仍然存在答案的可能性。
题解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int search (int * nums, int numsSize, int target) { int low=0 ; int high=numsSize; while (low<high) { int mid=low+(high-low)/2 ; if (*(nums+mid)>target) high=mid; else if (*(nums+mid)<target) low=mid+1 ; else return mid; } return -1 ; }
2. 第一个错误的版本 描述 你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
分析 直接使用二分查找即可
题解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int firstBadVersion (int n) { int low=1 ; int high=n; while (low<high) { int mid=low+(high-low)/2 ; if (isBadVersion (mid)) high=mid; else low=mid+1 ; } return low; }
3. 搜索插入位置 描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
分析 判断mid是否相等,相等则返回,直到发现不存在位置。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int searchInsert (int * nums, int numsSize, int target) { int low=0 ; int high=numsSize-1 ; int mid; if (target<*nums) return 0 ; if (target>*(nums+numsSize-1 )) return numsSize; while (low<high) { mid=low+(high-low)/2 ; if (*(nums+mid)>=target) high=mid; else if (*(nums+mid)<target) low=mid+1 ; else return mid; } return low; }
二分法总结 练习
34. 在排序数组中查找元素的第一个和最后一个位置 34. 在排序数组中查找元素的第一个和最后一个位置
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <iostream> #include <vector> #include <algorithm> using namespace std;class Solution { public : vector<int > searchRange (vector<int > &nums, int target) { vector<int > res; if (nums.size () == 0 ) { res.push_back (-1 ); res.push_back (-1 ); return res; } int low = 0 ; int high = nums.size () - 1 ; int mid; while (low < high) { mid = low + (high - low) / 2 ; if (nums.at (mid) >= target) high = mid; else if (nums.at (mid) < target) low = mid + 1 ; } if (nums.at (low) == target) res.push_back (low); else res.push_back (-1 ); high = nums.size () - 1 ; while (low < high) { mid = low + (high - low) / 2 + (high - low) % 2 ; if (nums.at (mid) <= target) low = mid; else high = mid - 1 ; } if (nums.at (low) == target) res.push_back (low); else res.push_back (-1 ); return res; } };
寻找所在分段然后在该分段内使用常规二分查找即可
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <iostream> #include <vector> #include <algorithm> using namespace std;class Solution { public : int search (vector<int > &nums, int target) { int low = 0 ; int high = nums.size () - 1 ; int mid; if (nums.at (low) > nums.at (high)) { while (low < high - 1 ) { mid = low + (high - low) / 2 ; if (nums.at (mid) > nums.at (low)) low = mid; else if (nums.at (mid) < nums.at (high)) high = mid; } if (target >= nums.at (0 ) && target <= nums.at (low)) { high = low; low = 0 ; } else if (target >= nums.at (high) && target <= nums.at (nums.size () - 1 )) { low = high; high = nums.size () - 1 ; } else return -1 ; } while (low <= high) { mid = low + (high - low) / 2 ; if (nums.at (mid) < target) low = mid + 1 ; else if (nums.at (mid) > target) high = mid - 1 ; else return mid; } return -1 ; } };
跟上一道题比起来,这道题的难点在于如何处理重复的元素。 //去除重复的影响 while ((low<high)&&(nums.at(++low) == nums.at(high)));
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 class Solution { public : bool search (vector<int > &nums, int target) { if (nums.size () == 1 ) return nums.at (0 ) == target; int low = -1 ; int high = nums.size () - 1 ; int mid; while ((low<high)&&(nums.at (++low) == nums.at (high))); if (nums.at (low) > nums.at (high)) { while (low < high - 1 ) { mid = low + (high - low) / 2 ; if (nums.at (mid) == target) return true ; if (nums.at (mid) >= nums.at (low)) low = mid; else if (nums.at (mid) <= nums.at (high)) high = mid; } if (target >= nums.at (0 ) && target <= nums.at (low)) { high = low; low = 0 ; } else if (target >= nums.at (high) && target <= nums.at (nums.size () - 1 )) { low = high; high = nums.size () - 1 ; } else return false ; } while (low <= high) { mid = low + (high - low) / 2 ; if (nums.at (mid) < target) low = mid + 1 ; else if (nums.at (mid) > target) high = mid - 1 ; else return true ; } return false ; } };
没什么难度,就是判断是否分界以及分界点
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution { public : int findMin (vector<int > &nums) { if (nums.size () == 1 ) return nums.at (0 ); int low = 0 ; int high = nums.size () - 1 ; int mid; if (nums.at (low) > nums.at (high)) { while (low < high - 1 ) { mid = low + (high - low) / 2 ; if (nums.at (mid) > nums.at (low)) low = mid; else if (nums.at (mid) < nums.at (high)) high = mid; } return nums.at (high); } return nums.at (low); } };
比上一道题多一个消除即可 //去除重复的影响 while ((low<high)&&(nums.at(++low) == nums.at(high)));
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Solution { public : int findMin (vector<int > &nums) { if (nums.size () == 1 ) return nums.at (0 ); int low = -1 ; int high = nums.size () - 1 ; int mid; while ((low<high)&&(nums.at (++low) == nums.at (high))); if (nums.at (low) > nums.at (high)) { while (low < high - 1 ) { mid = low + (high - low) / 2 ; if (nums.at (mid) >= nums.at (low)) low = mid; else if (nums.at (mid) <= nums.at (high)) high = mid; } return nums.at (high); } return nums.at (low); } };
首先想到使用动态规划将问题缩小到更小规模。考虑到以每一个元素为结尾的最长递增子序列与在其前面的所有元素的关系为 两个元素满足递增关系则当前元素的最长值的前面的元素的最长值加一 ,然后遍历当前元素的所有前元素即可找打最大值。
但是这种方法的时间复杂度为O(N^2)不好。
方法二:
贪心加二分,首先维护一个最长递增子序列的长度len,一个记录每一个长度的递增子序列的最后一个元素的最小值的数组dp[n],显然这个数组递增。然后遍历nums,若nums[i]大于dp[len]则说明当前最长的递增子序列还可以在其后面加上一个nums[i]来使得长度加一。否则的话在dp中寻找一个位置使得dp[j]为第一个大于nums[i]的。这可以使用二分来寻找。
代码 方法一 动态规划 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : int lengthOfLIS (vector<int >& nums) { vector<int > dp; dp.push_back (1 ); int MAX=1 ; for (int i=1 ;i<=nums.size ()-1 ;i++) { int max=1 ; for (int j=i-1 ;j>=0 ;j--) if (nums.at (i)>nums.at (j)) if (dp.at (j)+1 >max) max=dp.at (j)+1 ; dp.push_back (max); MAX=max>MAX?max:MAX; } return MAX; } };
代码 方法二 贪心加二分 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class Solution { public : int lengthOfLIS (vector<int > &nums) { vector<int > dp = {0 }; int len = 1 ; dp.push_back (nums.at (0 )); int low; int high; int mid; int cur; for (int i = 1 ; i <= nums.size () - 1 ; i++) { cur = nums.at (i); if (cur > dp.at (len)) { dp.push_back (cur); len++; continue ; } low = 1 ; high = len; mid; while (low < high) { mid = low + (high - low) / 2 ; if (cur < dp.at (mid)) high = mid; else if (cur > dp.at (mid)) low = mid + 1 ; else low = high = mid; } dp.at (high) = cur; } return len; } };
没什么好说的,注意边界即可
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution { public : int peakIndexInMountainArray (vector<int > &arr) { int low = 0 ; int high = arr.size () - 1 ; int mid; int left_value; int mid_value; int right_valule; while (low < high) { mid = low + (high - low) / 2 ; if (mid == 0 ) return 1 ; left_value = arr.at (mid - 1 ); mid_value = arr.at (mid); right_valule = arr.at (mid + 1 ); if (left_value < mid_value && mid_value < right_valule) low = mid + 1 ; else if (left_value > mid_value && mid_value > right_valule) high = mid - 1 ; else return mid; } return low; } };
这个也好,首先利用上一题找到山峰的位置,然后分段二分查找即可,先左后右
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 class Solution { public : int findInMountainArray (int target, MountainArray &mountainArr) { int low = 0 ; int high = mountainArr.length () - 1 ; int mid; int left_value; int mid_value; int right_valule; int peak; int res = -1 ; while (low < high) { mid = low + (high - low) / 2 ; if (mid == 0 ) { low=1 ; break ; } left_value = mountainArr.get (mid - 1 ); mid_value = mountainArr.get (mid); right_valule = mountainArr.get (mid + 1 ); if (left_value < mid_value && mid_value < right_valule) low = mid + 1 ; else if (left_value > mid_value && mid_value > right_valule) high = mid - 1 ; else { low = mid; break ; } } peak=low; low = 0 ; high = peak; while (low < high) { mid = low + (high - low) / 2 ; mid_value = mountainArr.get (mid); if (target < mid_value) high = mid - 1 ; else if (target > mid_value) low = mid + 1 ; else return mid; } if (mountainArr.get (low)==target) return low; low = peak + 1 ; high = mountainArr.length () - 1 ; while (low < high) { mid = low + (high - low) / 2 ; mid_value = mountainArr.get (mid); if (target > mid_value) high = mid - 1 ; else if (target < mid_value) low = mid + 1 ; else return mid; } if (mountainArr.get (low)==target) return low; return -1 ; } };
代码 未定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 class Solution { public : double findMedianSortedArrays (vector<int > &nums1, vector<int > &nums2) { if (nums1.size () == 0 ) { int index = nums2.size () - 1 ; if (index % 2 == 0 ) { return (double )nums2.at (index / 2 ); } else { return (double )(nums2.at (index / 2 ) + nums2.at (index / 2 + 1 )); } } else if (nums2.size () == 0 ) { int index = nums1.size () - 1 ; if (index % 2 == 0 ) { return (double )nums1.at (index / 2 ); } else { return (double )(nums1.at (index / 2 ) + nums1.at (index / 2 + 1 )); } } int low1 = 0 ; int high1 = nums1.size () - 1 ; int low2 = 0 ; int high2 = nums2.size () - 1 ; bool isodd=(high1+high2)%2 ; double res; int mid1; int mid2; int mid1_value; int mid2_value; while (low1 < high1 || low2 < high2) { mid1 = low1 + (high1 - low1) / 2 ; mid1_value = nums1.at (mid1); mid2 = low2 + (high2 - low2) / 2 ; mid2_value = nums2.at (mid2); if (mid1_value <= mid2_value) { low1 = mid1; int low = low1; int high = high1; int mid; int mid_value; while (low < high) { mid = low + (high - low) / 2 +(high-low)%2 ; mid_value = nums1.at (mid); if (mid2_value > mid_value) low = mid; else if (mid2_value <= mid_value) high = mid - 1 ; } high1 = high; high2 = mid2; low = low2; high = high2; while (low < high) { mid = low + (high - low) / 2 ; mid_value = nums2.at (mid); if (mid1_value >= mid_value) low = mid + 1 ; else if (mid1_value < mid_value) high = mid; } low2 = low; } else { low2 = mid2; int low = low2; int high = high2; int mid; int mid_value; while (low < high) { mid = low + (high - low) / 2 +(high-low)%2 ; mid_value = nums2.at (mid); if (mid1_value > mid_value) low = mid; else if (mid1_value <= mid_value) high = mid - 1 ; } high2 = high; high1 = mid1; low = low1; high = high1; while (low < high) { mid = low + (high - low) / 2 ; mid_value = nums1.at (mid); if (mid2_value >= mid_value) low = mid + 1 ; else if (mid2_value < mid_value) high = mid; } low1 = low; } } mid1_value=nums1.at (low1); mid2_value=nums2.at (low2); if (isodd) return (double )mid1_value>mid2_value?mid1_value:mid2_value; else return (double )(mid1_value+mid2_value); return -1 ; } };
day 2 这个也没有难度,首先使用二分找到绝对值最小的数,然后使用双指针双向前进即可。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class Solution { public : vector<int > sortedSquares (vector<int > &nums) { int low = 0 ; int high = nums.size () - 1 ; int mid; vector<int > res; if (nums.at (0 ) >= 0 ) { for (int i = 0 ; i <= nums.size () - 1 ; i++) res.push_back (nums.at (i) * nums.at (i)); return res; } if (nums.at (nums.size () - 1 ) <= 0 ) { for (int i = nums.size () - 1 ; i >= 0 ; i--) res.push_back (nums.at (i) * nums.at (i)); return res; } int positive=nums.size ()-1 ; int negative=0 ; while (positive-negative>1 ) { mid=negative+(positive-negative)/2 ; if (nums.at (mid)<0 ) negative=mid; else positive=mid; } while (positive<=nums.size ()-1 &&negative>=0 ) { if (nums.at (positive)+nums.at (negative)>=0 ) res.push_back (nums.at (negative)*nums.at (negative--)); else res.push_back (nums.at (positive)*nums.at (positive++)); } while (positive<=nums.size ()-1 ) res.push_back (nums.at (positive)*nums.at (positive++)); while (negative>=0 ) res.push_back (nums.at (negative)*nums.at (negative--)); return res; } };
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Solution { public : void rotate (vector<int > &nums, int k) { int low = 0 ; int high = nums.size () - 1 ; int temp; k = k % nums.size (); rotate1 (nums, low, high); rotate1 (nums, 0 , k - 1 ); rotate1 (nums, k, nums.size () - 1 ); } void rotate1 (vector<int > &nums, int low, int high) { int temp; while (low < high) { temp = nums.at (low); nums.at (low) = nums.at (high); nums.at (high) = temp; low++; high--; } } };
day 3 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { public : void moveZeroes (vector<int > &nums) { int size = nums.size (); int t1 = 0 ; int t2 = 0 ; while (t2 < size) { if (nums.at (t2)!=0 ) { if (t1!=t2) swap (nums.at (t1),nums.at (t2)); t1++; } t2++; } } };
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution {public : vector<int > twoSum (vector<int >& numbers, int target) { int low=0 ; int high=numbers.size ()-1 ; vector<int > res; while (1 ) { int sum=numbers.at (low)+numbers.at (high); if (sum>target) high--; else if (sum<target) low++; else { res.push_back (low+1 ); res.push_back (high+1 ); return res; } } return res; } };
day 4 code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution {public : void reverseString (vector<char >& s) { int low=0 ; int high=s.size ()-1 ; while (low<high) { swap (s.at (low),s.at (high)); low++; high--; } } };
code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { public : string reverseWords (string s) { int low = 0 ; int high = 0 ; int size = s.length (); while (high < size) { low = high; while (high < size && s.at (high) != ' ' ) high++; int i = low; int j = high - 1 ; while (i < j) swap (s.at (i++), s.at (j--)); high++; } return s; } };
day 5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public : ListNode* middleNode (ListNode* head) { ListNode*high=head; ListNode*low=head; while (high->next!=NULL ) { high=high->next; low=low->next; high=high->next; if (high==NULL ) return low; } return low; } };
code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution {public : ListNode* removeNthFromEnd (ListNode* head, int n) { vector<ListNode*> v; ListNode * it=head; while (it) { v.push_back (it); it=it->next; } int count=v.size (); int index=count-n; printf ("%d %d \n" ,count,index); if (index==0 ) { return head->next; } if (index==count-1 ) { v.at (count-2 )->next=NULL ; return head; } v.at (index-1 )->next=v.at (index+1 ); return head; } };
这种方法太傻了,使用双指针即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution {public : ListNode* removeNthFromEnd (ListNode* head, int n) { ListNode*prehead=new ListNode (-1 ,head); ListNode*high=prehead; ListNode*low=prehead; for (int i=1 ;i<=n+1 ;i++) high=high->next; while (high!=NULL ) { high=high->next; low=low->next; } low->next=low->next->next; return prehead->next; } };
但事实上由于双指针法需要大量的访问内存,这导致其访问内存的次数是方法一的一倍多,故其速度并不快。内存真的好慢。
day 6 code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Solution {public : int lengthOfLongestSubstring (string s) { int len=s.length (); if (len==0 ) return 0 ; vector<int > dp; dp.push_back (1 ); int max=1 ; for (int i=1 ;i<=len-1 ;i++) { char cur=s.at (i); int it=i-1 ; int w=dp.at (i-1 ); int length; while (w>0 &&it>=0 ) { w=w<dp.at (it)?w:dp.at (it); if (s.at (it)==cur) { break ; } w--; it--; } length=i-it; dp.push_back (length); max=length>max?length:max; } return max; } };