“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击查看活动详情”
220913-双指针
1. 相关题目
这两道题真是用了好久(─.─||)
本来想着用倒叙的暴力解法,在循环之前先过滤掉大于target的数,但这样的方法完完全全忽略了数组首部可能存在的负数。
这道题有几种解法:
-
首尾指针法(缩减搜索空间)
int i = 0; int j = numbers.size() - 1; while(numbers[i] + numbers[j] != target) { if(numbers[i] + numbers[j] > target) j--; else i++; }这样一来,时间复杂度为O(n),因为额外建立了长度为2的数组,空间复杂度为O(1)。
-
hashmap法(可以针对一般的无序数组)
vector<int> twoSum(vector<int>& nums, int target) { vector<int> v(2); unordered_map<int, int> map; for (int i = 0; i < nums.size(); ++i) { auto found = map.find(target - nums.at(i)); if (found != map.end()) { v.at(0) = found->second + 1; v.at(1) = i + 1; return v; } map.insert(pair<int, int>(nums.at(i), i)); } return v; }时间复杂度O(n) ,空间复杂度O(n)。
-
一次遍历法
使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。(参考了快速排序的思想)
int i = 0, j = 0; while(j < nums.size()) { if(nums[j]) { swap(nums[i], nums[j]); i++; } j++; }时间复杂度为O(n), 空间复杂度为O(1)。
-
两次遍历法
第一次遍历记录非0的个数,第二次遍历将末尾填0。
int count = 0; for(int i = 0; i < nums.size(); i++) { if(nums[i]) nums[count++] = nums[i]; } for(int i = count; i < nums.size(); i++) { nums[i] = 0; }时间复杂度为O(n),空间复杂度为O(1)。
额外学到
- unordered_map容器是使用hashTable实现的,平均而言,搜索、插入和删除的时间复杂度为 O(1)。
220914-双指针
220915-双指针
-
单指针法
双次遍历,第一次count,第二次指向中间节点。
int count = 0; ListNode* i= head; while(i != NULL) { count++; i = i->next; } int mid = count/2; ListNode* j = head; while(mid--) { j = j->next; } return j;时间复杂度为O(N),空间复杂度为O(1),只需要常数空间存放变量和指针。
-
建立指针数组法
vector<ListNode*> arr; while(head != NULL) { arr.push_back(head); head = head->next; } return arr[arr.size() / 2];时间复杂度为O(N),空间复杂度为O(N)。
-
快慢指针法
slow指针走一步,fast指针走两步。
ListNode* slow = head; ListNode* fast = head; while(fast != NULL && fast->next != NULL) { slow = slow->next; fast = fast->next->next; } return slow;时间复杂度为O(N),空间复杂度为O(1),只需常数空间存放两个指针。
本题的一种情况是删除头指针,对此需要做额外的处理。在对链表进行操作时,一种常见的作法是引入一个哑节点,其next指向头指针,这样就不用再对删除头指针的情况做额外的处理了。
-
单指针法(对标上题的单指针法)
ListNode* dummy = new ListNode(0, head); ListNode* i = dummy; // count int num = count - n - 1; while(num--) { i = i->next; } i->next = i->next->next; head = dummy->next; // 注意这里不能直接返回head delete(dummy); return head;时间复杂度为O(N),空间复杂度为O(1)。
-
栈(对标上题的指针数组法)
将节点全部入栈,在出栈N个节点后,栈顶指针即为被删元素的前一个节点。
ListNode* dummy = new ListNode(0, head); stack<ListNode*> stk; ListNode* cur = dummy; while(cur) { stk.push(cur); cur = cur->next; } while(n--) { stk.pop(); } cur = stk.top(); cur->next = cur->next->next; head = dummy->next; delete(dummy); return head;时间复杂度为O(N),空间复杂度为O(N)。
-
双指针法(对标上题的快慢指针法)
ListNode* dummy = new ListNode(0, head); ListNode* j = dummy; ListNode* i = dummy; while(n--) j = j->next; while(j->next != NULL) { i = i->next; j = j->next; } i->next = i->next->next; head = dummy->next; delete(dummy); return head;时间复杂度为O(N),空间复杂度为O(1)。
周赛310
使用unordered_map。
本周总结
本周依旧专注于双指针的算法题,其中167. 两数之和 II - 输入有序数组的首尾指针法和876. 链表的中间结点的快慢指针法都给我提供了很大的灵感。
本周刷的题依然不多,但贵在坚持,冲冲冲!