文章目录
4.寻找两个正序数组的中位数
二分查找
- 使用
while(true)
循环代替递归函数传参,使用两个idx
避免修改数组
int getKthElement(const vector<int>& nums1, const vector<int>& nums2, int k)
{
int m = nums1.size();
int n = nums2.size();
int idx1 = 0, idx2 = 0;
while (true)
{
// cout<<"idx1,idx2="<<idx1<<' '<<idx2<<endl;
// cout<<"k="<<k<<endl;
// 边界情况
if (idx1 == m) // nums1空,返回nums2中第(idx1+k-1)小的值
{
return nums2[idx2 + k - 1];
}
else if (idx2 == n)
{
return nums1[idx1 + k - 1];
}
if (k == 1)
{
return min(nums1[idx1], nums2[idx2]);
}
// 一般情况
int newIdx1 = min(idx1 + k / 2 - 1, m - 1);
int newIdx2 = min(idx2 + k / 2 - 1, n - 1);
int tmp1 = nums1[newIdx1], tmp2 = nums2[newIdx2];
if (tmp1 <= tmp2)
{
// 注意这两行的顺序
k -= (newIdx1 - idx1 + 1);
idx1 = newIdx1 + 1;
}
else
{
k -= (newIdx2 - idx2 + 1);
idx2 = newIdx2 + 1;
}
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
int total = nums1.size() + nums2.size();
if (total % 2 == 1)
{
return (double)getKthElement(nums1, nums2, (total) / 2 + 1);
}
else
{
return ((double)getKthElement(nums1, nums2, (total / 2)) + (double)getKthElement(nums1, nums2, (total / 2) + 1)) / 2;
}
}
15.三数之和
排序+三重循环暴力方法
要求三元组不重复,于是
-
保证元组内部的单调性(非严格)
-
if(j==i+1||nums[j]!=nums[j-1])=true
时进行下一组循环,否则可能出现-4,-1,-1,0,1,2
中
[-1,0,1]
出现两次
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
int len=nums.size();
vector<vector<int>> res;
int idx=0;
for(int i=0;i<len;i++){
if(i==0||nums[i]!=nums[i-1]){
for(int j=i+1;j<len;j++){
if(j==i+1||nums[j]!=nums[j-1]){
for(int k=j+1;k<len;k++){
if(k==j+1||nums[k]!=nums[k-1]){
if(nums[i]+nums[j]+nums[k]==0){
vector<int> v;res.push_back(v);
res[idx].push_back(nums[i]);
res[idx].push_back(nums[j]);
res[idx].push_back(nums[k]);
idx++;
}
}
}
}
}
}
}
return res;
}
排序+双指针
对于给定的
a
,
b
a,b
a,b,
c
c
c是确定的。当右移二重循环的指针,即
b
b
b变大时,
c
c
c将变小,也就是三重循环的指针左移。在这种一个指针递增、一个指针递减的情况下,可以使用双指针,通常为left
和right
,将枚举的复杂度从
O
(
N
2
)
O(N^2)
O(N2)降到
O
(
N
)
O(N)
O(N).
算法分析
- 特判,当数组长度<3时返回空,当
nums[0]>0
时返回空 - 对数组进行排序
- 遍历,
- 当
nums[i]>0
跳出循环 - 在结束每个
i
的比较后,跳过重复元素 - 构造双指针,
left=i+1,right=n-1
,当left<right
时执行循环x+y+z<0
,右移left
x+y+z>0
,座椅right
x+y+z=0
,保存结果,分别将左右指针移动到下一个不等值位置
- 当
vector<vector<int>> threeSum(vector<int>& nums)
{
vector<vector<int>> res;
if (nums.size() < 3) // 数组长度小于3
return res;
sort(nums.begin(), nums.end());
if (nums[0] > 0) // 最小的元素大于0
return res;
int i = 0;
while (i < nums.size())
{
if (nums[i] > 0) // 最小的元素大于0,提前终止循环
break;
int left = i + 1, right = nums.size() - 1;
while (left < right)
{
long long x = nums[i];
long long y = nums[left];
long long z = nums[right];
if (x + y + z < 0)
{
left++;
}
else if (x + y + z > 0)
{
right--;
}
else if (x + y + z == 0)
{
// res.push_back({x,y,z}); // 这样push进的是longlong
res.push_back({nums[i], nums[left], nums[right]});
// 相同的y,z不应再出现
while (left < right && nums[left + 1] == nums[left])
{
left++;
}
while (left < right && nums[right - 1] == nums[right])
{
right--;
}
// 此时到了最后一个与原y,z相等的地方,最后移动一位
left++;
right--;
}
}
while (i + 1 < nums.size() && nums[i + 1] == nums[i])
{
i++; // 跳过重复的x
}
// 此时到了最后一个与原x相等的地方,最后移动一位
i++;
}
return res;
}
19.删除链表的倒数第N个结点
栈
需要自定义一个表头,防止head
被删去后返回错误
得到目标删除结点的前一个结点
ListNode* removeNthFromEnd(ListNode* head, int n)
{
ListNode* top = new ListNode(0, head);
stack<ListNode*> st;
ListNode* cur = top;
while (cur)
{
st.push(cur);
cur = cur->next;
}
for (int i = 0; i < n; i++)
{
st.pop();
}
ListNode* pre = st.top();
ListNode* tmp = pre->next;
pre->next = tmp->next;
// delete tmp;
return top->next;
}
双指针
first
指针从第
n
n
n个结点出发,second
从第
0
0
0个结点出发,则当first
到达表尾NULL
时,second
到达倒数第
n
n
n个结点。
为了方便起见,同样需要得到目标删除结点的前一个结点,即second
从表头哑结点dummy
出发。
ListNode* removeNthFromEnd(ListNode* head, int n)
{
// 表头
ListNode* dummy = new ListNode(-1, head);
ListNode* first = head;
ListNode* second = dummy;
for (int i = 0; i < n; i++)
{
first = first->next;
}
while (first)
{
first = first->next;
second = second->next;
}
second->next = second->next->next;
return dummy->next;
}
75.颜色分类
单指针两次循环
void sortColors(vector<int>& nums)
{
int n = nums.size();
int ptr = 0; // 0~(ptr-1)都是0
for (int i = 0; i < n; i++)
{
if (nums[i] == 0)
{
swap(nums[i], nums[ptr]);
ptr++;
}
}
// 0~(ptr-1)都是0和1
for (int i = ptr; i < n; i++)
{
if (nums[i] == 1)
{
swap(nums[i], nums[ptr]);
ptr++;
}
}
}
双指针(0,1)一次循环
- 扫到1,交换
ptr1
和i
,ptr1
右移 - 扫到0,如果头部0的右侧有连续的1,则
ptr0
指向1,交换ptr0
和i
将使1被挪到后面,所以要将i
位交换回ptr1
- 第二个判断处可以用
else if
,也可以用if
- 第二个判断处可以用
void sortColors(vector<int>& nums)
{
int n = nums.size();
int ptr0 = 0, ptr1 = 0;
for (int i = 0; i < n; i++)
{
if (nums[i] == 1)
{
swap(nums[i], nums[ptr1]);
ptr1++;
}
if (nums[i] == 0)
{
swap(nums[i], nums[ptr0]);
if (ptr0 < ptr1) // 如果0后面是连续的1,会让1被交换出去
swap(nums[i], nums[ptr1]);
ptr0++;
ptr1++;
}
}
}
双指针(0,2)一次循环
-
i
扫到ptr2
为止 -
扫到 0 0 0,交换
ptr0
和i
,ptr0
右移 -
扫到 2 2 2,交换
ptr2
和i
,ptr2
左移。由于交换ptr2
和i
时可能使得nums[i]
还是 2 2 2(例如[2,1,2]
),所以需要不断地交换直到nums[i]
不为 2 2 2
void sortColors(vector<int>& nums)
{
int n = nums.size();
int ptr0 = 0, ptr2 = n - 1;
for (int i = 0; i < n; i++)
{
while (i <= ptr2 && nums[i] == 2)
{
swap(nums[i], nums[ptr2]);
ptr2--;
}
if (nums[i] == 0)
{
swap(nums[i], nums[ptr0]);
ptr0++;
}
}
}
76.最小覆盖子串
基本滑动窗口
设置左右指针,滑动右指针至包含所有 t t t中字符,再滑动左指针夹出最小窗口,记录窗口大小。
int book[127] = {0};
int bookt[127] = {0};
string minWindow(string s, string t)
{
// 特判
if (s.length() < t.length())
return "";
if (s.length() == t.length())
{
if (!s.compare(t))
return s;
}
// 扫入bookt,注意t中各元素可能出现不止一次
for (int i = 0; i < t.length(); i++)
{
bookt[t[i]]++;
}
int left = 0, right = 0;
int finalLeft = 0;
int res = s.length() + 1;
while (right < s.length())
{
book[s[right]]++;
int flag = 1; // 是否包含t中所有元素
for (int i = 0; i < t.length(); i++)
{
if (book[t[i]] < bookt[t[i]])
{
flag = 0;
break;
}
}
if (!flag)
{
right++;
}
else // 开始右滑left
{
while (left < right)
{
if (t.find(s[left]) == t.npos ||
(t.find(s[left]) != t.npos && book[s[left]] > bookt[s[left]]))
{
book[s[left]]--;
left++;
}
// 左指针已经到达最右端
else break;
}
int tmp = right - left + 1;
if (tmp < res)
{
res = tmp;
finalLeft = left;
}
// 右移窗口
book[s[left]]--;
left++;
right++;
}
}
// 没有出现过任何满足的子串
if (res == s.length() + 1)
res = 0;
return s.substr(finalLeft, res);
}
142.环形链表Ⅱ
哈希
ListNode *detectCycle(ListNode *head)
{
set<ListNode*> visited;
while (head)
{
if (visited.count(head))
{
return head;
}
visited.insert(head);
head = head->next;
}
return NULL;
}
双指针
slow
每次走一位,fast
每次走两位,如果链表中存在环,它们必将在环中相遇。
ListNode *detectCycle(ListNode *head)
{
ListNode* slow = head;
ListNode* fast = head;
while (fast)
{
slow = slow->next;
if (!fast->next)
{
return NULL;
}
fast = fast->next->next;
if (fast == slow) // 相遇
{
ListNode* ptr = head;
while (ptr != slow)
{
ptr = ptr->next;
slow = slow->next
}
return ptr;
}
}
return NULL;
}
148.排序链表
插入排序
维护 lastSorted
为链表的已排序部分的最后一个节点,更新cur
时使用cur=lastSorted->next
,这一点与数组不同。如果使用cur=cur->next
,则由于cur->next
可能已经改变,无法指向想要的结点。
ListNode* sortList(ListNode* head)
{
if (head)
{
ListNode* dummy = new ListNode(0, head);
ListNode* lastSorted = head; // 已排序部分的最后一个结点
ListNode* cur = head->next;
while (cur)
{
if (cur->val >= lastSorted->val)
{
lastSorted = cur;
}
else
{
ListNode* pre = dummy;
while (pre->next->val <= cur->val)
{
pre = pre->next;
}
lastSorted->next = cur->next;
cur->next = pre->next;
pre->next = cur;
}
cur = lastSorted->next;
}
return dummy->next;
}
return head;
}
归并排序
自顶向下
时间复杂度为 n O ( log n ) nO(\log n) nO(logn),空间复杂度为 O ( log n ) O(\log n) O(logn)
用快慢指针查找中点:快指针一次走两个,慢指针一次走一个,当快指针到达表尾时慢指针到达中点
在
Merge
中的while(tmp1)
和while(tmp2)
块,由于待合并链表长度差至多为 1 1 1,可以用if
代替while
ListNode* sortList(ListNode* head)
{
return sortList(head, NULL);
}
ListNode* sortList(ListNode* head, ListNode* tail)
// 排序[head,tail)
{
if (!head)
return head;
if (head->next == tail)
// 仅有head一个元素
{
head->next = NULL;
return head;
}
// 用快慢指针找到链表中点
ListNode* slow = head, *fast = head;
while (fast != tail)
{
slow = slow->next;
fast = fast->next;
if (fast != tail)
fast = fast->next;
}
ListNode* mid = slow;
ListNode* head1 = sortList(head, mid);
ListNode* head2 = sortList(mid, tail);
ListNode* res = Merge(head1, head2);
return res;
}
ListNode* Merge(ListNode* head1, ListNode* head2)
{
ListNode* dummy = new ListNode(0);
ListNode* tmp = dummy, *tmp1 = head1, *tmp2 = head2;
while (tmp1 && tmp2)
{
if (tmp1->val <= tmp2->val)
{
tmp->next = tmp1;
tmp1 = tmp1->next;
}
else
{
tmp->next = tmp2;
tmp2 = tmp2->next;
}
tmp = tmp->next;
}
while (tmp1)
{
tmp->next = tmp1;
tmp1 = tmp1->next;
tmp = tmp->next;
}
while (tmp2)
{
tmp->next = tmp2;
tmp2 = tmp2->next;
tmp = tmp->next;
}
return dummy->next;
}
自底向上
时间复杂度为 n O ( log n ) nO(\log n) nO(logn),空间复杂度为 O ( 1 ) O(1) O(1)
ListNode* sortList(ListNode* head)
{
if (!head)
return head;
int len = 0;
ListNode* tmp = head;
while (tmp)
{
len++;
tmp = tmp->next;
}
ListNode* dummy = new ListNode(0, head);
for (int subLen = 1; subLen < len; subLen *= 2)
{
ListNode* pre = dummy, *cur = dummy->next;
while (cur)
{
// 获得一个长为subLen的子串
ListNode* head1 = cur;
for (int i = 1; i < subLen && cur->next; i++)
cur = cur->next;
ListNode* head2 = cur->next;
cur->next = NULL;
// 获得第二个长为subLen的子串
cur = head2;
for (int i = 1; i < subLen && cur && cur->next; i++)
cur = cur->next;
ListNode* next = NULL;
if (cur)
{
next = cur->next;
cur->next = NULL;
}
// 更新pre(结果链表)
// 更新cur(过程链表)
ListNode* merged = Merge(head1, head2);
pre->next = merged;
while (pre->next)
pre = pre->next;
cur = next;
}
}
return dummy->next;
}
234.回文链表
注意比较时比较node->val
而不是node
数组
将结点值压进数组,左右指针向内逐个收缩
bool isPalindrome(ListNode* head)
{
vector<int> v;
ListNode* p = head;
while (p)
{
v.push_back(p->val);
p = p->next;
}
// 注意int强制转换
for (int i = 0, j = (int)v.size() - 1; i < j; i++, j--)
{
if (v[i] != v[j])
{
return false;
}
}
return true;
}
递归
利用堆栈特性,将栈顶的cur
指针指向表尾,将全局指针font
初始化为表头
ListNode* font;
bool isPalindromeRecursive(ListNode* cur)
{
if (cur)
{
if (!isPalindromeRecursive(cur->next))
return false;
if (font->val != cur->val)
return false;
font = font->next;
}
return true;
}
bool isPalindrome(ListNode* head)
{
font = head;
return isPalindromeRecursive(head);
}
283.移动零
双指针
void moveZeroes(vector<int>& nums)
{
int n = nums.size();
int left = 0, right = 0;
while (right < n)
{
if (nums[right])
{
swap(nums[left], nums[right]);
left++;
}
right++;
}
}
直接修改做法
void moveZeroes(vector<int>& nums)
{
int n = nums.size();
int idx = 0;
for (int i = 0; i < n; i++)
{
if (nums[i])
{
nums[idx++] = nums[i];
}
}
for (int i = idx; i < n; i++)
nums[i] = 0;
}