概述
1.数组中的第k个最大元素,利用快排的partition思想,加上二分检索
2.比特位计数,直观的方法时间复杂度为o(n*sizeof(int)),有效降低时间复杂度的方法是判断当前数字的奇偶性,再与前一个联系,得出结论。
3.最长上升子序列,动态规划o(n^2),贪心+二分o(nlogn)。维护一个数组,新进来的如果比数组最后的大,插在后面,否则找到第一个比新进来的大的元素,替换掉他。这个查找是属于有序数组中的查找,可以采用折半查找。
4.根据身高重建队列,这题先根据身高和人数排序(用到了sort的复杂排序,自定义排序规则)。然后再根据人数往数组中的合适位置插。
5.寻找重复数,o(logn)使用二分查找,判断左右数字出现的个数。
6.环形链表,查找链表的环的入口。两种方法unordered_set和快慢指针。快慢指针主要是推导公式,证明,为什么A = (n-1)(B+C)+C
1.数组中第k个最大元素,一遍过,莫名其妙
题目描述:给定一个无序数组,找出这个数组中第k大的数字。
思路:快排的Partition函数
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
class Solution {
public:
void swap(int& a,int& b){int temp = a;a = b;b = temp;}
int Partition(vector<int>& nums,int start,int end){
int pivotKey = nums[start];
while(start<end){
while(pivotKey<=nums[end]&&start<end) end--;
swap(nums[start],nums[end]);
while(pivotKey>=nums[start]&&start<end) start++;
swap(nums[start],nums[end]);
}
return start;
}
int findKthLargest(vector<int>& nums, int k) {
int pp = nums.size()-k;
int end = nums.size()-1;
int pivot = Partition(nums,0,end);
while(pp!=pivot){
if(pp<pivot){pivot = Partition(nums,0,pivot-1);}
else{pivot = Partition(nums,pivot+1,end);}
}
return nums[pp];
}
};
2. 比特位计数
题目描述:给定一个整数,从0到这个整数之间所有的数的二进制中含1的个数,放入对应数组。
要求,时间o(n).....自己实现的o(nlogn).
时间复杂度为o(n)的思路:
通过奇偶性来判断1的个数。
1.如果n为奇数,那么他的1的个数等于前一个数的加一
2.如果n为偶数,那么他的1的个数等于他一般的数的1的个数。。。。。。(很关键!!!!!!)
int countCore(int number){
int count = 0;
while(number){
count++;
number = (number-1)&number;
}
return count;
}
vector<int> countBits(int num) {
vector<int> res(num+1,0);
for(int i = 1;i<=num;++i){
res[i] = countCore(i);
}
return res;
}
//o(n)解法
class Solution {
public:
vector<int> countBits(int num) {
vector<int> res(num+1,0);
for(int i = 1;i<=num;++i){
if(i&1 == 1) res[i] = res[i-1]+1;
else{
res[i] = res[i/2];
}
}
return res;
}
};
3.最长上升子序列(较难)
题目描述:给定一个无序的数组,求他的最长子序列,注意子序列是可以不连续的!要求时间复杂度o(nlogn)
思路:动态规划o(n^2),二分查找+贪心o(nlogn)
动态规划:
1.定义状态。。
dp[i]表示以第i个数字为结尾的“最长上升子序列”的长度。在[0,1,...,i]的范围内,选择以数字nums[i]为结尾可获得的最长上升子序列的长度。nums[i]必须要被选取。初始化,可将dp全部初始化为1。定义输入,题目不是求状态,所以把dp数组看一遍,找最大的。
2.推导“状态转移方程”。
dp[i] = max{1+dp[j] for j<i if nums[j]<nums[i]}
二分查找+贪心:
1.维护一个升序数组,存放的规则是,如果数组元素来了,判断是否比数组的最后一个元素大。
2.在遍历数组过程中,如果比数组的最后一个元素大,那么加载数组后面,数组长度加一
3.否则就要找到这个元素在这个升序数组中的存放位置,如果已经存在,那就不更新,如果位于俩数中间,替换后一个数。
这一步使用二分查找。(尽量使得辅助数组的结尾更小,这就会有更大的可能性构造更长的上升子序列)
4.tail的长度即为结果
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len = nums.size();
if(len<2) return len;
vector<int> temp;
temp.push_back(nums[0]);
int end = 0;
for(int i = 1;i<len;++i){
if(nums[i]>temp[end]){
temp.push_back(nums[i]);
end++;
}
else{
int start = 0;
int right = end;
while(start<right){
int mid = (start+right)>>1;
if(temp[mid]<nums[i]) start = mid+1;
else right = mid;
}
temp[start] = nums[i];
}
}
return end+1;
}
};
4.根据身高重建队列
题目描述:给定一组数据,存放的是数值对[x,y],x表示此人的身高,y表示在此人前面还有y个身高大于等于他的人。要求对这些无序的数据进行排序。
思路:
1.建立辅助数组,把最高的人先挑出来,按照次数升序排序。。。sort(people.begin(),people.end(),[](vector<int>&a,vector<int>&b){...}) //在这里学会使用sort进行随心所欲的排序。
2.从候选数组中挑选出最高的人,插入辅助数组的合适位置。for(auto &e:people){res.insert(res.begin()+e[1],e);}
class Solution {
public:
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
//先排序,再一个一个插入
sort(people.begin(),people.end(),[](const vector<int>&a,const vector<int>&b){
if(a[0] > b[0]) return true;
if(a[0] == b[0]&&a[1]<b[1]) return true;
return false;
});
vector<vector<int>> res;
for(auto &e:people){
res.insert(res.begin()+e[1],e);
}
return res;
}
};
5.寻找重复数287.....关键是边界条件
题目描述:给定一个大小为n+1的数组,里面放着从1到n的数字,假设只有一个数字是重复的,找出他,他可能重复好多次。
要求以o(1)的空间复杂度,小于o(n^2)的空间复杂度,不能改变原数组,这个数字有可能重复出现n次。
思路:无,,,想到了剑指offer上的二分定位重复数字
1.以n/2为界限,统计小于n/2的数字个数
2.如果数字个数大于n/2,那就在左边,end = mid+1; 注意边界条件[),
3.反之再右边,start = mid; 注意边界条件[]
class Solution {
public:
int findDuplicate(vector<int>& nums) {
//先排序,再双指针....不行,不能改变原数组
//用二分查找
int len = nums.size();
int start = 0;
int end = len-1;
while(start<end){
int mid = (start+end)>>1;
int count = 0;
for(auto num:nums){
if(num<=mid) count++;
}
if(count>mid) end = mid;
else start = mid+1;
}
return start;
}
};
6.环形链表II
题目描述:给定一个链表,判断是否有环,并找到环的入口。
思路:1.快慢指针2.(别人的)unordered_set代码简短
快慢指针:
1.在第一次相遇时,慢指针走过的路程为:slow=A+B。快指针走过的路程为:fast=A+n(B+C)+B。
2.因为快指针走过的距离一定是慢指针的二倍,所以有2(A+B) = A+n(B+C)+B。化简为A = (n-1)(B+C)+C
3.将快指针指向head,并且前进步长改为1。当走改进后的快指针走了A步,则一定会与慢指针再入口处重合。
unordered_set:
1.定义unordered_set。
2.只要head不为空,并且set中没有出现过head,那就往set里面加。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
// unordered_set<ListNode*> myset;
// while(head){
// if(myset.count(head)>0) return head;
// else myset.insert(head);
// head = head->next;
// }
// return nullptr;
ListNode* pfast = head;
ListNode* pslow = head;
ListNode* ptemp = head;
bool hasCycle = false;
while(pfast != nullptr && pfast->next != nullptr){
pslow = pslow->next;
pfast = pfast->next->next;
if(pslow == pfast){
hasCycle = true;
while(hasCycle && ptemp != pslow){
ptemp = ptemp->next;
pslow = pslow->next;
}
break;
}
}
return hasCycle?pslow:nullptr;
}
};
来源:https://blog.csdn.net/weixin_42151929/article/details/100799454