文章目录
- 数组中的第K个最大元素
- 快排
- k个一组反转链表
- 解法一:栈
- 解法二:模拟
- 买卖股票最佳时机
- 买卖股票最佳时机i
- 买卖股票最佳时机ii
- 贪心
- 无重复长度子串
- 最长递增子序列
- 只出现一次的数字 III
- lru缓存
- 合并K个升序链表
数组中的第K个最大元素
快排
class Solution {
public:
void quickPartition(vector<int>& nums, int start, int end, int target) {
// 随机取一个数作为基准
int random = (end - start) + start;
int base = nums[random];
// 将该数放到待快排区间开头第一个元素
swap(nums[start], nums[random]);
int index = start;
// 从待快排区间的第二个元素开始,依次与base比较,如果大于等于base则将该元素
// 交换到index + 1位置,index++,使得最终index前面的元素都比base大。
for (int i = start + 1; i <= end; ++i) {
if (nums[i] >= base) {
swap(nums[index + 1], nums[i]);
index++;
}
}
// base存放在区间开头,现在需要把它交换到index位置,这就是它在整个有序数组中的位置。
swap(nums[index], nums[start]);
// 如果index小于target,需要在右边区间继续快排查找,否则到在边区间查找,
// 如果等于已经找到目标值不需要递归,这里这么做优化了传统快排的复杂度。
if (index < target) {
quickPartition(nums, index + 1, end, target);
}
else if (index > target) {
quickPartition(nums, start, index - 1, target);
}
}
int findKthLargest(vector<int>& nums, int k) {
// 方法1. 快速排序的分区思想,快排的思想是一次找出一个数的正确位置,
// 并使得该数左边的元素都比它小,该数右边的元素都比它大,要找出第k
// 大的元素,只需要在快排的时候采用降序排序,找到下标为k-1的元素即可。
quickPartition(nums, 0, nums.size() - 1, k - 1);
return nums[k - 1];
}
};
k个一组反转链表
解法一:栈
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
if (k <= 1)//首先判断k的合法性
return head;
stack<ListNode *> myStack;//辅助栈,用于转换k个节点的转换
int count = 0;//进栈的节点数
ListNode *ptr = head;
//进栈k个节点
while (count < k && ptr != NULL){
myStack.push(ptr);
ptr = ptr->next;
++count;
}
if (count < k){//如果不足k个,则无需转换
return head;
}
//出栈并且顺序串接好
//pHead是逆置结果的表头,pEnd是逆置结果的临时表尾
ListNode *pHead = myStack.top(), *pEnd;
myStack.pop();
pEnd = pHead;
while (count > 1){
pEnd->next = myStack.top();
pEnd = pEnd->next;
myStack.pop();
--count;
}
//递归调用,将ptr后面的链表进行逆置
pEnd->next = reverseKGroup(ptr, k);
return pHead;
}
};
解法二:模拟
思路:可以把整段链表分为
- 已反转
- 即将反转但未反转
- 未反转
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
if (!head) {
return head;
}
int loop = 0; //计数第几个节点,用于判断是否
ListNode* p = head;
ListNode* pHead = p;
ListNode* pLast = p;
while (p) {
++loop;
if (loop % k == 0) {
ListNode* temp = reverse(pHead, p);
if (loop == k) {
head = temp; //第一次到达k时,此时返回的head就是最终的head
} else {
pLast->next = temp; //非第一次到达k时,把刚反转的那一段链表的头节点接到上一次已反转的尾部
}
p = pHead;
pHead = p->next;
pLast = p;
}
p = p->next;
}
return head;
}
ListNode* reverse(ListNode* head, ListNode* last) { //给定一个头节点一个尾节点,反转链表
ListNode* realhead = last->next; //把未到达的那个节点连接到尾部,便于后面正常遍历
while (realhead != last) {
ListNode* p = head->next;
head->next = realhead;
realhead = head;
head = p;
}
return realhead;
}
};
买卖股票最佳时机
买卖股票最佳时机i
- dp:维护最大利润,维护最小买入值
class Solution {
public:
int maxProfit(vector<int>& prices) {
int maxAns = 0;
int minPrice = 99999;
for (int i = 0; i < prices.size(); i++) {
minPrice = min(minPrice, prices[i]);
maxAns = max(maxAns,prices[i] - minPrice);
}
return maxAns;
}
};
买卖股票最佳时机ii
贪心
- 对于 「今天的股价 - 昨天的股价」,得到的结果有 3 种可能:① 正数,② 000,③负数。贪心算法的决策是: 只加正数 。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int maxP = 0;
for (int i = 1; i < prices.size(); i++) {
maxP += max(0, prices[i] - prices[i-1]);
}
return maxP;
}
};
无重复长度子串
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map <char, int> map; //
int left = 0, size = s.length();
int ans = 1, right = 1;
map[s[left]] = 1;
if (!size) {
return 0;
}
while (right < size) {
if (map.find(s[right]) != map.end()) { //如果遍历的这个字符在map里,把left位置的字符从map里删除,并right不动,left++
ans = max(ans, right-left);
map.erase(s[left]);
++left;
} else { //当遍历的这个字符不存在map里,就往后走,并把该字符添加到map里
map[s[right]] = 1;
++right;
}
}
ans = max(ans, right-left);
return ans;
}
};
最长递增子序列
维护dp数组里的最大值
维护已遍历原数组里最大值,每次与最大值比较,如果大于max,就让dp数组的该索引值为++max。否则还为max
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> dp(nums.size(), 1);
dp[0] = 1;
int ans = 1;
for (int i = 1; i < nums.size(); i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = max(dp[i], dp[j]+1);
ans = max(ans, dp[i]);
}
}
}
return ans;
}
};
只出现一次的数字 III
利用一个hashmap,出现一次的保留在map里,出现第二次就从map里删除
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
unordered_map <int, int> map;
vector<int> ans;
for (int i = 0; i < nums.size(); i++) {
if (map.find(nums[i]) != map.end()) {
map.erase(nums[i]);
} else {
map[nums[i]] = 1;
}
}
for (auto& it : map) {
ans.push_back(it.first);
}
return ans;
}
};
lru缓存
struct linkList { //创建双向链表
int key, value;
linkList* next;
linkList* prev;
};
class LRUCache {
public:
int size;
int capacity;
linkList* head; //保留住头节点位置,头节点的下一个为最新的
linkList* last; //保留尾节点,尾节点的上一个为最旧的
unordered_map <int, linkList*> map;
LRUCache(int capacity) { //初始化
size = 0;
this->capacity = capacity;
head = new linkList;
last = new linkList;
head->next = last;
last->prev = head;
}
int get(int key) {
if (map.find(key) == map.end()) { //未找到
return -1;
} else {
moveToFirstNode(map[key]); //移到头节点后面
return map[key]->value;
}
}
void put(int key, int value) {
if (map.find(key) != map.end()) { //如果在map里找到了该key,就从value保存的节点修改值就好,然后再移到首部
map[key]->value = value;
moveToFirstNode(map[key]);
return;
}
if (size < capacity) { //缓存够的话,直接放进首部
map[key] = addNode(key, value);
++size;
} else {
removeNode();
map[key] = addNode(key, value); //缓存不够,需要移除尾节点,再把新添加的节点放到首部
}
}
linkList* addNode(int key, int value) {
linkList* p = new linkList; //设值
p->value = value;
p->key = key;
head->next->prev = p; //头插法
p->next = head->next;
p->prev = head;
head->next = p;
return p;
}
void removeNode() { //移除最后一个节点
linkList* pRemove = last->prev;
linkList* temp = last->prev->prev;
temp->next = last;
last->prev = temp;
map.erase(pRemove->key);
delete pRemove;
}
void moveToFirstNode(linkList* p) { //把节点p从中间断开再放入首部
linkList* temp = p->prev; //断开,并把分隔开的上下两个节点连接好
linkList* pNext = p->next;
temp->next = pNext;
pNext->prev = temp;
head->next->prev = p; //放到首部
p->next = head->next;
p->prev = head;
head->next = p;
}
};
合并K个升序链表
我们需要维护当前每个链表没有被合并的元素的最前面一个,k 个链表就最多有 k 个满足这样条件的元素,每次在这些元素里面选取 val 属性最小的元素合并到答案中。在选取最小元素的时候,我们可以用优先队列来优化这个过程。
class Solution {
public:
struct comp { // 重载运算符
bool operator () (ListNode* a, ListNode* b) {
return a->val > b->val;
}
};
priority_queue<ListNode*, vector<ListNode*>, comp> q;
ListNode* mergeKLists(vector<ListNode*>& lists) {
for (auto node: lists) {
if (node) {
q.push(node);
}
}
ListNode* head = new ListNode();
ListNode* tail = head;
while (!q.empty()) {
ListNode* node = q.top();
q.pop();
tail->next = node;
tail = tail->next;
if (node->next) {
q.push(node->next);
}
}
return head->next;
}
};