53 最大子数组和
dp滚动数组:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int pre = 0, maxAns = nums[0]; //以某一位为尾的最大和 最大和
for(const auto &x: nums){
pre = max(pre + x, x); //只有前缀+自己更大时才带上前缀
maxAns = max(maxAns, pre);
}
return maxAns;
}
};
基本思想完全一致的前缀和法:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int presum = 0;
int minsum = 0;
int maxsubsum = INT_MIN;
for(const auto &x: nums){
presum += x;
//注意顺序,要减的是上一次的最小前缀
maxsubsum = max(maxsubsum, presum - minsum);
minsum = min(minsum, presum);
}
return maxsubsum;
}
};
206 反转链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* curr = head; //当前待反转结点
ListNode* prev = nullptr; //原链表中curr的前一个结点
while (curr) {
ListNode* next = curr->next; //准备好下待反转结点
curr->next = prev; //反转
prev = curr; //下一个
curr = next; //下一个
}
return prev; //此时curr为nullptr,故返回prev
}
};
21 合并两个有序链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* dum = new ListNode(0); //空结点,next指向生成链表的头节点
ListNode* cur = dum;
while(l1 != nullptr && l2 != nullptr){ //哪个小接上哪个
if(l1->val < l2->val){
cur->next = l1;
l1 = l1->next;
}else{
cur->next = l2;
l2 = l2->next;
}
cur = cur->next;
}
cur->next = l1 != nullptr ? l1 : l2; //某个链表先遍历完了,直接接上另一链表剩下的即可
return dum->next; //返回头结点
}
};
3 无重复字符的最长子串
class Solution {
public:
int lengthOfLongestSubstring(string s) {
map<char, int> m;
int ans = 0, left = 0, right = 0; //所求长度 当前字符串左/右端
while (right < s.size()) { //通过研究以s[right]结尾的不重复字符串实现单次遍历
if (m.find(s[right]) != m.end()) { //不为m.end()表示s[right]在前面出现过
//不光要考虑当前重复的,也得考虑之前重复过的,若left > m[s[right]] + 1,
//则说明当前字母虽然对于整个字符串而言有重复,但在当前字符串中并不
left = max(left, m[s[right]] + 1);
}
m[s[right++]] = right;
ans = max(right - left, ans);
}
return ans;
}
};
215 数组中的第K个最大元素
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int, vector<int>, greater<int>> pq; //升序优先队列,从头到尾从小到大
for (auto n : nums) {
if (pq.size() == k && pq.top() >= n) continue; //队列已满,n更小则忽略
if (pq.size() == k) {//n更大,弹出队头最小的一个
pq.pop();
}
pq.push(n);
}
return pq.top();
}
};
(手撕快排+随机种子 / 堆排太麻烦了。。。后面补——2022.3.13)
2022.3.14补:
快速选择算法
class Solution {
public:
int quickSelect(vector<int>& arr, int left, int right, int index) {
int q = randomPartition(arr, left, right);
if(q == index) {
return arr[q];
}else { //快速选择算法,递归,类似快排
return q < index? quickSelect(arr, q + 1, right, index) : quickSelect(arr, left, q - 1, index);
}
}
inline int randomPartition(vector<int>& arr, int left, int right) {
int r = rand() % (right - left + 1) + left;
swap(arr[r], arr[right]); //待选择的放到末尾,最后再换回去
return partition(arr, left, right);
}
inline int partition(vector<int>& arr, int left, int right) {
int x = arr[right], k = left - 1;
for(int i = left; i < right; ++i) {
if(arr[i] >= x) {
swap(arr[++k], arr[i]); //注意是++k
}
}
swap(arr[k + 1], arr[right]);
return k + 1;
}
int findKthLargest(vector<int>& nums, int k) {
srand(time(0)); //随机数种子
return quickSelect(nums, 0, nums.size() - 1, k - 1);
}
};
快速选择算法的时间复杂度和空间复杂度分别为O(n)和O(log n)。
堆排序算法
class Solution {
public:
void minHeapify(vector<int>& arr, int h, int k) { //小顶堆空间复杂度更小
int left = h * 2 + 1, right = h * 2 + 2, minimum = h;
if (left < k && arr[left] < arr[minimum]) {
minimum = left;
}
if (right < k && arr[right] < arr[minimum]) {
minimum = right;
}
if (minimum != h) {
swap(arr[h], arr[minimum]);
minHeapify(arr, minimum, k);
}
}
int findKthLargest(vector<int>& nums, int k) {
for (int i = (k - 1) / 2; i >= 0; --i) { //把数组前k项调整为小根堆
minHeapify(nums, i, k);
}
for (int j = k; j < nums.size(); ++j) {
if (nums[j] > nums[0]) {
swap(nums[j], nums[0]);
minHeapify(nums, 0, k);
}
}
return nums[0];
}
};
堆排序的时间复杂度和空间复杂度分别为O(nlogk)和O(logk)。