合并有序链表 力扣链接
将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的。
输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <iostream> using namespace std;struct ListNode { int val; ListNode* next; ListNode (int x) : val (x), next (nullptr ) {} }; ListNode* merge (ListNode* l1, ListNode* l2) { if (l1 == nullptr ) return l2; if (l2 == nullptr ) return l1; ListNode* dummy = new ListNode (0 ); ListNode* node = dummy; while (l1 != nullptr && l2 != nullptr ) { if (l1->val < l2->val) { node->next = l1; l1 = l1->next; } else { node->next = l2; l2 = l2->next; } node = node->next;; } if (l1 == nullptr ) node->next = l2; if (l2 == nullptr ) node->next = l1; return dummy -> next; } int main (void ) { ListNode* node0 = new ListNode (0 ); ListNode* node1 = new ListNode (1 ); ListNode* node2 = new ListNode (2 ); ListNode* node3 = new ListNode (3 ); ListNode* node4 = new ListNode (1 ); ListNode* node5 = new ListNode (4 ); node0->next = node1; node1->next = node2; node2->next = node3; node3->next = nullptr ; node4->next = node5; node5->next = nullptr ; auto node = merge (node0, node4); while (node != nullptr ) { cout << node->val << endl; node = node->next; } return 0 ; }
反转链表 定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <algorithm> #include <unordered_map> #include <iostream> #include <vector> using namespace std;struct ListNode { int val; ListNode* next; ListNode (int x) : val (x), next (nullptr ) {} }; ListNode* ReverseList (ListNode* pHead) { if (pHead == nullptr || pHead->next == nullptr ) return pHead; ListNode* dummy = new ListNode (0 ); ListNode* pre =dummy; pre->next = pHead; ListNode* cur = pHead->next; pHead->next = nullptr ; ListNode* temp = nullptr ; while (cur != nullptr ) { temp = cur; cur = cur->next; temp->next = pre->next; pre->next = temp; } return dummy -> next; }
快速排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void quickSort (vector<int >& data, int low, int high) { if (low >= high) return ; int key = data[low], begin = low, end = high; while (begin < end) { while (begin < end && data[end] > key) { end--; } if (begin < end) data[begin++] = data[end]; while (begin < end && data[begin] <= key) { begin++; } if (begin < end) data[end--] = data[begin]; } data[begin] = key; quickSort (data, low, begin - 1 ); quickSort (data, begin + 1 , high); }
归并排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 void mergeSort (vector<int >& data, vector<int >& copy, int begin, int end) { if (begin >= end) return ; int mid = begin + (end - begin) / 2 ; int low1 = begin, high1 = mid, low2 = mid + 1 , high2 = end; int index = begin; mergeSort (copy, data, low1, high1); mergeSort (copy, data, low2, high2); while (low1 <= high1 && low2 <= high2) { copy[index++] = data[low1] < data[low2] ? data[low1++] : data[low2++]; } while (low1 <= high1) copy[index++] = data[low1++]; while (low2 <= high2) copy[index++] = data[low2++]; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void mergeSort2 (vector<int >& data, int begin, int end) { if (begin >= end) return ; int mid = begin + (end - begin) / 2 ; int low1 = begin, high1 = mid, low2 = mid + 1 , high2 = end; int index = 0 ; mergeSort2 (data, low1, high1); mergeSort2 (data, low2, high2); vector<int > help (end - begin + 1 ) ; while (low1 <= high1 && low2 <= high2) { help[index++] = data[low1] < data[low2] ? data[low1++] : data[low2++]; } while (low1 <= high1) help[index++] = data[low1++]; while (low2 <= high2) help[index++] = data[low2++]; for (int i = 0 ; i < help.size (); i++) { data[begin + i] = help[i]; } }
实现一个堆排序 堆排序过程:
将n个元素的序列构建一个大顶堆或小顶堆
将堆顶的元素放到序列末尾
将前n-1个元素重新构建大顶堆或小顶堆,重复这个过程,直到所有元素都已经排序
整体时间复杂度为O(nlogn),空间复杂度O(1)
注:堆排序是不稳定 的排序算法,是一种树形选择排序。恰好利用树形的特点保存了部分前面的比较结果,因此可以减少比较次数。不适合记录较少的排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <iostream> #include <vector> using namespace std;void adjust (vector<int >& arr, int len, int index) { int maxid = index; int left = 2 * index + 1 , right = 2 * index + 2 ; if (left < len && arr[left] > arr[maxid]) maxid = left; if (right < len && arr[right] > arr[maxid]) maxid = right; if (maxid != index) { swap (arr[maxid], arr[index]); adjust (arr, len, maxid); } } void heapsort (vector<int >& arr, int len) { for (int i = (len - 1 - 1 ) / 2 ; i >= 0 ; i--) { adjust (arr, len, i); } for (int i = len - 1 ; i > 0 ; i--) { swap (arr[0 ], arr[i]); adjust (arr, i, 0 ); } } int main () { vector<int > arr = { 3 ,4 ,2 ,1 ,5 ,8 ,7 ,6 }; cout << "before: " << endl; for (int item : arr) cout << item << " " ; cout << endl; heapsort (arr, arr.size ()); cout << "after: " << endl; for (int item : arr)cout << item << " " ; cout << endl; return 0 ; }
设计LRU缓存 力扣链接(opens new window)
设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。
它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
样例:
1 2 3 4 5 6 7 8 9 10 11 LRUCache cache = new LRUCache ( 2 ); cache.put (1 , 1 ); cache.put (2 , 2 ); cache.get (1 ); cache.put (3 , 3 ); cache.get (2 ); cache.put (4 , 4 ); cache.get (1 ); cache.get (3 ); cache.get (4 );
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 #include <iostream> #include <unordered_map> using namespace std;struct DoubleList { int key, val; DoubleList* pre, *next; DoubleList (int _key, int _val) : key (_key), val (_val), pre (nullptr ), next (nullptr ) { } }; class LRU {private : int capcity; DoubleList* head, *tail; unordered_map<int , DoubleList*> memory; public : LRU (int _capcity) { this ->capcity = _capcity; head = new DoubleList (-1 , -1 ); tail = new DoubleList (-1 , -1 ); head->next = tail; tail->pre = head; } ~LRU () { if (head != nullptr ) { delete head; head = nullptr ; } if (tail != nullptr ) { delete tail; tail = nullptr ; } for (auto & a : memory) { if (a.second != nullptr ) { delete a.second; a.second = nullptr ; } } } void put (int _key, int _val) { if (memory.find (_key) != memory.end ()) { DoubleList* node = memory[_key]; removeNode (node); node->val = _val; pushNode (node); return ; } if (memory.size () == this ->capcity) { int topKey = head->next->key; removeNode (head->next); memory.erase (topKey); } DoubleList* node = new DoubleList (_key, _val); pushNode (node); memory[_key] = node; } int get (int _key) { if (memory.find (_key) != memory.end ()) { DoubleList* node = memory[_key]; removeNode (node); pushNode (node); return node->val; } return -1 ; tail->pre->next = node; node->pre = tail->pre; node->next = tail; tail->pre = node; } }; int main () { LRU cache (2 ) ; cache.put (1 , 1 ); cache.put (2 , 2 ); cout << cache.get (1 ) << endl; cache.put (3 , 3 ); cout<< cache.get (2 ) << endl; cache.put (4 , 4 ); cout << cache.get (1 ) << endl; cout << cache.get (3 ) << endl; cout << cache.get (4 ) << endl; return 0 ; }
重排链表 力扣链接
给定一个单链表 L:L0→L1→…→Ln-1→Ln , 将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 #include <iostream> #include <string> #include <vector> #include <algorithm> #include <unordered_map> using namespace std;struct ListNode { int val; ListNode* next; ListNode (int _val) : val (_val), next (nullptr ) { } }; ListNode* myReverseList (ListNode* head) { if (head == nullptr || head->next == nullptr ) return head; ListNode* dummy = new ListNode (0 ); ListNode* pre = dummy; pre->next = head; ListNode* cur = head->next; head->next = nullptr ; ListNode* node = nullptr ; while (cur != nullptr ) { node = cur; cur = cur->next; node->next = pre->next; pre->next = node; } return dummy->next; } ListNode* myMerge (ListNode* p1, ListNode* p2) { if (p1 == nullptr ) return p2; if (p2 == nullptr ) return p1; ListNode* dummy = new ListNode (0 ); ListNode* pre = dummy; while (p1 != nullptr && p2 != nullptr ) { pre->next = p1; p1 = p1->next; pre = pre->next; pre->next = p2; p2 = p2->next; pre = pre->next; } if (p1 != nullptr ) pre->next = p1; return dummy->next; } ListNode* myReverOrderList (ListNode* head) { if (head == nullptr || head->next == nullptr ) return head; ListNode* slow = head, * fast = head->next; while (fast != nullptr && fast->next != nullptr ) { slow = slow->next; fast = fast->next->next; } ListNode* second = slow->next; slow->next = nullptr ; second = myReverseList (second); head = myMerge (head, second); return head; } int main () { ListNode* head = new ListNode (1 ); ListNode* node1 = new ListNode (2 ); ListNode* node2 = new ListNode (3 ); ListNode* node3 = new ListNode (4 ); ListNode* node4 = new ListNode (5 ); ListNode* node5 = new ListNode (6 ); head->next = node1; node1->next = node2; node2->next = node3; node3->next = node4; node4->next = node5; node5->next = nullptr ; head = myReverOrderList (head); while (head != nullptr ) { cout << head->val << endl; head = head->next; } return 0 ; }
奇偶链表 力扣链接
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。
1 2 3 4 5 6 示例 1 : 输入: 1 ->2 ->3 ->4 ->5 ->NULL 输出: 1 ->3 ->5 ->2 ->4 ->NULL 示例 2 : 输入: 2 ->1 ->3 ->5 ->6 ->4 ->7 ->NULL 输出: 2 ->3 ->6 ->7 ->1 ->5 ->4 ->NULL
注意:应当保持奇数节点和偶数节点的相对顺序。链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。
法一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ListNode* oddEvenList (ListNode* head) { if (head == nullptr || head->next == nullptr ) return head; ListNode* first = head; ListNode* second = head->next; ListNode* cur = second; while (second != nullptr && second->next != nullptr ) { first->next = second->next; second->next = first->next->next; first = first->next; second = second->next; } first->next = cur; return head; }
法二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ListNode* oddEvenList (ListNode* head) { if (head == nullptr ) return head; ListNode* p = head; ListNode* q = head->next; ListNode* evenhead = q; while (q != nullptr && q->next != nullptr ) { p->next = p->next->next; p = p->next; q->next = q->next->next; q = q->next; } p->next = evenhead; return head; }
Top K问题 Top K问题的常见形式:
给定10000个整数,找到第K大(第K小)的数
给定10000个整数,找出最大(最小)的前K个数
给定100000个单词,求前K词频的单词
解决Top K问题若干种方法:
使用最大最小堆。求最大的数用最小堆,求最小的数用最大堆。
Quick Select算法。使用类似快排的思路,根据pivot划分数组。
使用排序方法,排序后再寻找top K元素。
使用选择排序的思想,对前K个元素部分排序。
将1000…..个数分成m组,每组寻找top K个数,得到m×K个数,在这m×k个数里面找top K个数
使用最大最小堆的思路(以top K 最大元素为例)
按顺序扫描这10000个数,先取出K个元素构建一个大小为K的最小堆。每扫描到一个元素,如果这个元素大于堆顶的元素(这个堆最小的一个数),就放入堆中,并删除堆顶的元素,同时整理堆。如果这个元素小于堆顶的元素,就直接pass。最后堆中剩下的元素就是最大的前Top K个元素,最右的叶节点就是Top 第K大的元素。
注意:最小堆的插入时间复杂度为log(n), n为堆中元素个数,在这里是K。最小堆的初始化时间复杂度是nlog(n)
C++中的最大最小堆要用标准库的priority_queue来实现。
1 2 3 4 5 6 7 8 9 10 11 12 struct Node { int value; int idx; Node (int v, int i) : value (v), idx (i) {} friend bool operator < (const struct Node &n1, const struct Node &n2); }; inline bool operator < (const struct Node &n1, const struct Node &n2) { return n1.value < n2.value; } priority_queue<Node> pq;
使用Quick Select的思路(以寻找第K大的元素为例)
Quick Select脱胎于快速排序,提出这两个算法的都是同一个人。算法的过程是这样的: 首先选取一个枢轴,然后将数组中小于该枢轴的数放到左边,大于该枢轴的数放到右边。 此时,如果左边的数组中的元素个数大于等于K,则第K大的数肯定在左边数组中,继续对左边数组执行相同操作; 如果左边的数组元素个数等于K-1,则第K大的数就是pivot; 如果左边的数组元素个数小于K,则第K大的数肯定在右边数组中,对右边数组执行相同操作。
这个算法与快排最大的区别是,每次划分后只处理左半边或者右半边,而快排在划分后对左右半边都继续排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 int findKthLargest (vector<int >& nums, int k) { return quickSelect (nums, k, 0 , nums.size () - 1 ); } int quickSelect (vector<int >& nums, int k, int left, int right) { if (left == right) return nums[right]; int index = partition (nums, left, right); if (index - left + 1 > k) { return quickSelect (nums, k, left, index - 1 ); } else if (index - left + 1 == k) { return nums[index]; } else { return quickSelect (nums, k - (index - left + 1 ), index + 1 , right); } } int partition (vector<int >& nums, int left, int right) { int begin = left, end = right; int key = nums[begin]; while (begin > end) { while (begin > end && nums[end] < key) { end--; } if (begin > end) nums[begin++] = nums[end]; while (begin > end && nums[begin] >= key) { begin++; } if (begin > end) nums[end--] = nums[begin]; } nums[begin] = key; return begin; } int partition2 (vector<int >& arr, int low, int high) { int i = low, j = high, pivot = arr[low]; while (i < j) { while (i < j && arr[j] > pivot) j--; while (i < j && arr[i] <= pivot) i++; if (i < j) { swap (arr[i++], arr[j--]); } } if (arr[i] > pivot) { swap (arr[low], arr[i - 1 ]); return i - 1 ; } swap (arr[i], arr[low]); return i; }
使用选择排序的思想对前K个元素排序 ( 以寻找前K大个元素为例) 扫描一遍数组,选出最大的一个元素,然后再扫描一遍数组,找出第二大的元素,再扫描一遍数组,找出第三大的元素。。。。。以此类推,找K个元素,时间复杂度为O(N*K)
写三个线程交替打印ABC 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <iostream> #include <thread> #include <mutex> #include <condition_variable> using namespace std;mutex mymutex; condition_variable cv; int flag=0 ;void printa () { unique_lock<mutex> lk (mymutex) ; int count=0 ; while (count<10 ){ while (flag!=0 ) cv.wait (lk); cout<<"thread 1: a" <<endl; flag=1 ; cv.notify_all (); count++; } cout<<"my thread 1 finish" <<endl; } void printb () { unique_lock<mutex> lk (mymutex) ; for (int i=0 ;i<10 ;i++){ while (flag!=1 ) cv.wait (lk); cout<<"thread 2: b" <<endl; flag=2 ; cv.notify_all (); } cout<<"my thread 2 finish" <<endl; } void printc () { unique_lock<mutex> lk (mymutex) ; for (int i=0 ;i<10 ;i++){ while (flag!=2 ) cv.wait (lk); cout<<"thread 3: c" <<endl; flag=0 ; cv.notify_all (); } cout<<"my thread 3 finish" <<endl; } int main () { thread th2 (printa) ; thread th1 (printb) ; thread th3 (printc) ; th1.join (); th2.join (); th3.join (); cout<<" main thread " <<endl; }