- 判断是否是环形链表
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL || head->next == NULL)
return false;
ListNode *behind = head;
ListNode *ahead = head->next; //双指针不一定是一个在前一个在后
while(behind != ahead)
{
if(ahead->next!=NULL && ahead->next->next != NULL)
{
ahead = ahead->next->next;
behind = behind->next;
}
else
return false;
}
return true;
}
};
- 最长子序列
给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。
class Solution {
public:
string findLongestWord(string s, vector<string>& d) {
int d_size = d.size(), s_ind=0;
int i1=0,i2=0,j1=s.size(),j2;
int max_length=0;
string target="";
for(;s_ind<d_size;s_ind++)
{
i1=0,i2=0;
string the_sting = d[s_ind];
j2=the_sting.size();
while(i1<j1)
{
if(s[i1]==the_sting[i2])
{
i1++;
i2++;
}
else
{
i1++;
}
}
if(i2==j2)
{
if(j2>max_length)
{
max_length=j2;
target = the_sting;
}
else if(j2==max_length) //第n次把条件判断中的==写成=
{
if(the_sting<target) //C++可以直接这样判断所谓的字典顺序
target = the_sting;
}
}
}
return target;
}
};
- 找到倒数第 k 小的元素。
快速排序和堆排序都可以用于找到倒数第k小的元素。找倒数第k小的元素要用大顶堆。当堆中节点数量大于k时,如果此时新插入的元素小于堆顶元素,则删除堆顶元素,从新插入当前元素。插入和删除堆顶元素的时间复杂度都是logN。
事实上,快拍和堆排还能用于topK问题。对于快排,当找到kth元素时,遍历整个数组,则能得到topK。堆排中保留的K个元素就是topK。(显然堆排的数组中不能有重复的元素。)
快排:
class Solution {
public:
int quickSelect(vector<int>& a, int l, int r, int index) {
int q = partition(a, l, r);
if (q == index) {
return a[q];
} else {
return q < index ? quickSelect(a, q + 1, r, index) : quickSelect(a, l, q - 1, index);
}
}
inline int partition(vector<int>& a, int l, int r) {
int x = a[l];
while(l<r)
{
while(a[r]>=x && l<r)
r--;
if(l<r)
swap(a[l],a[r]);
while(a[l]<=x && l<r)
l++;
if(l<r)
swap(a[l],a[r]);
}
a[l]=x;
return l;
}
int findKthLargest(vector<int>& nums, int k) {
return quickSelect(nums, 0, nums.size() - 1, nums.size() - k);
}
};
时间复杂度为O(n),空间复杂度O(logN).
最小堆(找最大topk,用最小堆)
class Solution {
public:
void maxHeapify(vector<int>& a, int i, int heapSize) {
int l = i * 2 + 1, r = i * 2 + 2, largest = i;
if (l < heapSize && a[l] > a[largest]) {
largest = l;
}
if (r < heapSize && a[r] > a[largest]) {
largest = r;
}
if (largest != i) {
swap(a[i], a[largest]);
maxHeapify(a, largest, heapSize);
}
}
void buildMaxHeap(vector<int>& a, int heapSize) {
for (int i = heapSize / 2; i >= 0; --i) {
maxHeapify(a, i, heapSize);
}
}
int findKthLargest(vector<int>& nums, int k) {
int heapSize = nums.size();
buildMaxHeap(nums, heapSize);
for (int i = nums.size() - 1; i >= nums.size() - k + 1; --i) {
swap(nums[0], nums[i]);
--heapSize;
maxHeapify(nums, 0, heapSize);
}
return nums[0];
}
};
- 出现频率最多的 k 个元素
4.1 基于堆的实现
一开始的想法是建立hash table,然后把value作为堆的元素。找到最大的k个频率。然后再去遍历hash table,找出对应的key。实际上可以直接把pair作为堆的元素。
c++中堆有现成的实现,即priority_queue优先队列。
c++优先队列(priority_queue)用法详解
class Solution {
public:
static bool cmp(pair<int, int>& m, pair<int, int>& n) {
return m.second > n.second;
}
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> occurrences; //unorder_map与map的区别是map内部
//由红黑树实现,按key排序
for (auto& v : nums) { //基于范围的for loop
occurrences[v]++;
}
// pair 的第一个元素代表数组的值,第二个元素代表了该值出现的次数
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> q(cmp);
for (auto& [num, count] : occurrences) {
if (q.size() == k) {
if (q.top().second < count) {
q.pop();
q.emplace(num, count); //等价于q.push(pair(num, count))
}
} else {
q.emplace(num, count);
}
}
vector<int> ret;
while (!q.empty()) {
ret.push_back(q.top().first);
q.pop();
}
return ret;
}
};
4.2 基于快排的实现(bug版,测试未通过)
bug没有找到。。。
class Solution {
public:
void qsort(vector<pair<int, int>>& v, int start, int end, int k) {
if (start==end)
return;
pair<int, int> flag = v[start];
int old_start=start, old_end=end;
while(end>start)
{
while(v[end].second<=flag.second && end>start)
end--;
if(end>start)
swap(v[start], v[end]);
while(v[start].second>=flag.second)
start++;
if(end>start)
swap(v[start], v[end]);
}
v[start]=flag;
if(k==start-old_start || k==start-old_start-1)
return;
else if (k < start - old_start -1)
qsort(v, old_start, start - 1, k);
else
qsort(v, start + 1, old_end, k - (start -old_start + 1));
}
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> occurrences;
for (auto& v: nums) {
occurrences[v]++; //value默认为0
}
vector<pair<int, int>> values; //这个地方一定要用个vector来接收map,
//因为map无法根据i来索引
for (auto& kv: occurrences) {
values.push_back(kv);
}
//注意上面这种写法和下面这种写法的区别
//for(int i=0;i<s.size();i++)
// v.push_back(pair(s[i], m[s[i]]));
vector<int> ret;
qsort(values, 0, values.size() - 1, k);
for (int i = 0; i < k; i++)
{
ret.push_back(values[i].first);
}
return ret;
}
};
- 按照字符出现次数对字符串排序
5.1 快排版
class Solution {
public:
void quicksort(vector<pair<char,int>>& v, int i, int j)
{
if(i>=j)
return;
pair<char,int> flag = v[i];
int oi=i,oj=j;
while(i<j)
{
while(v[j].second <= flag.second && i<j)
j--;
if(i<j)
v[i++]=v[j];
while(v[i].second >= flag.second && i<j)
i++;
if(i<j)
v[j--]=v[i];
}
cout<<"i:"<<i<<endl;
cout<<"j:"<<j<<endl;
v[i] = flag;
quicksort(v, oi, i-1);
quicksort(v, i+1, oj);
}
string frequencySort(string s) {
string ans = s;
unordered_map<char, int> m;
for(auto& x:s)
m[x]++;
vector<pair<char,int>> v;
for(auto& kv : m)
v.push_back(kv);
quicksort(v,0,v.size()-1); //这个地方不是s.size()!!!!!
int i=0;
for(auto & kv:v) // 因为key有重复,所以不能i从0到s.size()来给ans[i]赋值
{
while(kv.second--)
{
ans[i] = kv.first;
i++;
}
}
return ans;
}
};
5.2 堆排
class Solution {
public:
static bool cmp(pair<char, int>& m, pair<char, int>& n) {
return m.second < n.second; //后面这个n代表top,m代表son
}
string frequencySort(string s) {
unordered_map<char,int> m;
for(auto& x:s)
m[x]++;
priority_queue<pair<char, int>, vector<pair<char, int>>, decltype(&cmp)> q(cmp);
for(auto& x:m)
{
q.push(x);
}
string ans = s;
int i=0;
while(!q.empty())
{
int temp = q.top().second;
char temp_char = q.top().first;
while(temp--)
{
ans[i++] = temp_char;
}
q.pop();
}
return ans;
}
};
第5题是一个全排序问题,显然会比找topk 和 kth问题要简单。但是都是可以用堆排和快排来做的。
第二天的任务第四天才完成,怎么这么菜的!!