训练题:数组排序

题目:http://t.cn/A6VvSNma

方法一:冒泡排序

  • 时间复杂度
    • 平均O(n2)
    • 最好O(n2)
    • 最差O(n2)
    • 时间复杂度完全不受数据初始顺序的影响,比如初始已经是有序的,依然要O(n2),这非常糟糕。
  • 空间复杂度:O(1)
  • 稳定性:稳定 ```cpp

vector MySort( vector &arr ) { if( arr.size() < 2 ) { return arr; }

  1. const auto size = arr.size();
  2. for( int i = 0; i < size - 1; i++ )
  3. {
  4. for( int j = 0; j < size - 1 - i; j++ )
  5. {
  6. if( arr[j] > arr[j + 1] ) { std::swap(arr[j], arr[j + 1]); }
  7. }
  8. }
  9. return arr;

}

  1. [基本内排序算法](https://www.yuque.com/tvvhealth/cs/vr8mgw?inner=zSQrw&view=doc_embed)
  2. <a name="rLj9b"></a>
  3. ## 方法二:插入排序
  4. - 时间复杂度
  5. - 平均:O(n2)
  6. - 最好:O(n),初始序列已经有序。因此小规模排序,插入排序最合适。
  7. - 最差:O(n2)
  8. - 空间复杂度:O(1)
  9. - 稳定性:稳定
  10. ```cpp
  11. vector<int> MySort( vector<int> &arr )
  12. {
  13. if( arr.size() < 2 ) { return arr; }
  14. const auto size = arr.size();
  15. for( int i = 1; i < size; i++ )
  16. {
  17. for( int j = i; j < size && arr[j] < arr[j - 1]; j-- )
  18. {
  19. std::swap(arr[j], arr[j - 1]);
  20. }
  21. }
  22. return arr;
  23. }

基本内排序算法

方法三:选择排序

  • 时间复杂度
    • 平均O(n2)
    • 最好O(n2)
    • 最差O(n2)
  • 空间复杂度:O(1)
  • 稳定性:不稳定 ```cpp

vector MySort( vector &arr ) { if( arr.size() < 2 ) { return arr; }

  1. const auto size = arr.size();
  2. int tmpIdx = -1;
  3. for( int i = 0; i < size - 1; i++ )
  4. {
  5. tmpIdx = i;
  6. for( int j = i + 1; j < size; j++ )
  7. {
  8. if( arr[j] < arr[tmpIdx] ) { tmpIdx = j; }
  9. }
  10. if( tmpIdx != i ) { std::swap(arr[i], arr[tmpIdx]); }
  11. }
  12. return arr;

}

  1. [基本内排序算法](https://www.yuque.com/tvvhealth/cs/vr8mgw?inner=qglHw&view=doc_embed)
  2. <a name="ywzS4"></a>
  3. ## 方法四:希尔排序
  4. - 时间复杂度
  5. - 平均O(n2)
  6. - 最差约O(n2)
  7. - 空间复杂度:O(1)
  8. - 稳定性:不稳定
  9. ```cpp
  10. vector<int> MySort( vector<int> &arr )
  11. {
  12. if( arr.size() < 2 ) { return arr; }
  13. const auto size = arr.size();
  14. for( int diff = size / 2; diff >= 1; diff /= 2 )
  15. {
  16. for( int start = 0; start < diff; start++ ) { _MySort( arr, start, diff ); }
  17. }
  18. return arr;
  19. }
  20. void _MySort( vector<int> &arr, const int &start, const int &diff )
  21. {
  22. const auto size = arr.size();
  23. for( int i = start + diff; i < size; i += diff )
  24. {
  25. for( int j = i; j > start && arr[j] < arr[j - diff]; j -= diff )
  26. {
  27. std::swap(arr[j], arr[j - diff]);
  28. }
  29. }
  30. }

基本内排序算法

方法五:归并排序

  1. vector<int> MySort( vector<int> &arr )
  2. {
  3. if( arr.size() < 2 ) { return arr; }
  4. vector<int> copyArr;
  5. copyArr.reserve( arr.size() );
  6. _MySort( arr, copyArr, 0, arr.size() - 1 );
  7. return arr;
  8. }
  9. void _MySort( vector<int> &arr, vector<int> &copyArr, const int &start, const int &end )
  10. {
  11. if( end <= start ) { return; }
  12. //此处可优化:if( ( end - start ) <= 100 ) 执行插入排序
  13. const int mid = ( start + end ) >> 1;
  14. _MySort( arr, copyArr, start, mid );
  15. _MySort( arr, copyArr, mid + 1, end );
  16. for( int i = start; i <= mid; i++ ) { copyArr[i] = arr[i]; }
  17. for( int i = end; i >= mid + 1; i-- ) { copyArr[mid + end + 1 - i] = arr[i]; }
  18. for( int tmpStart = start, tmpLeft = start, tmpRight = end; tmpStart <= end; tmpStart++ )
  19. {
  20. if( copyArr[tmpLeft] < copyArr[tmpRight] ) { arr[tmpStart] = copyArr[tmpLeft++]; }
  21. else { arr[tmpStart] = copyArr[tmpRight--]; }
  22. }
  23. }

基本内排序算法

方法六:快速排序

  1. vector<int> MySort( vector<int> &arr )
  2. {
  3. if( arr.size() < 2 ) { return arr; }
  4. _MySort( arr, 0, arr.size() - 1 );
  5. return arr;
  6. }
  7. void _MySort( vector<int> &arr, const int &start, const int &end )
  8. {
  9. if( end <= start ) { return; }
  10. // 此处可优化:if( ( end - start ) <= 100 ) 执行插入排序
  11. const int pivot = ( start + end ) >> 1;
  12. if( pivot != end ) { std::swap(arr[pivot], arr[end]); }
  13. int k = partition( arr, start - 1, end, arr[end] );
  14. if( k != end ) { std::swap(arr[k], arr[end]); }
  15. _MySort( arr, start, k - 1 );
  16. _MySort( arr, k + 1, end );
  17. }
  18. int partition( vector<int> &arr, int start, int end, const int &pivotVal )
  19. {
  20. while( true )
  21. {
  22. while( ++start != end && arr[start] < pivotVal );
  23. while( --end != start && !( arr[end] < pivotVal ) );
  24. if( start < end ) { std::swap(arr[start], arr[end]); }
  25. else { break; }
  26. }
  27. return start;
  28. }

基本内排序算法

方法七:堆排序

  1. vector<int> MySort( vector<int> &arr )
  2. {
  3. if( arr.size() < 2 ) { return arr; }
  4. Heap heap( arr );
  5. while( !heap.empty() ) { heap.pop(); }
  6. return arr;
  7. }
  8. class Heap
  9. {
  10. public:
  11. Heap( vector<int> &arr );
  12. void pop();
  13. bool empty();
  14. private:
  15. void siftDown( const int & );
  16. int leftChild( const int & );
  17. int rightChild( const int & );
  18. bool leaf( const int & );
  19. private:
  20. vector<int> &_data;
  21. int _dataSize;
  22. };
  23. Heap::Heap( vector<int> &arr )
  24. : _data( arr )
  25. , _dataSize( arr.size() )
  26. {
  27. for( int i = ( arr.size() >> 1 ) - 1; i >= 0; i-- ) { siftDown( i ); }
  28. }
  29. void Heap::pop()
  30. {
  31. if( _dataSize )
  32. {
  33. std::swap( _data[0], _data[_dataSize - 1] );
  34. _dataSize--;
  35. siftDown( 0 );
  36. }
  37. }
  38. bool Heap::empty() return _dataSize == 0;
  39. void Heap::siftDown( const int &index )
  40. {
  41. if( !leaf( index ) )
  42. {
  43. int maxChild = leftChild( index );
  44. int rightchild = rightChild( index );
  45. if( rightchild < _dataSize && _data[maxChild] < _data[rightchild] )
  46. {
  47. maxChild = rightchild;
  48. }
  49. if( _data[index] < _data[maxChild] )
  50. {
  51. std::swap( _data[maxChild], _data[index] );
  52. siftDown( maxChild );
  53. }
  54. }
  55. }
  56. int Heap::leftChild( const int &index ) return ( index << 1 ) + 1;
  57. int Heap::rightChild( const int &index ) return ( index << 1 ) + 2;
  58. bool Heap::leaf( const int &index ) return index > ( ( _dataSize >> 1 ) - 1 );

基本内排序算法

训练题:topK

场景一:找出topK个最小数

题目:http://t.cn/A6V7bQke

方法一:分治法

快速排序的partition方法。

  1. vector<int> getLeastNumbers(vector<int>& arr, int k) {
  2. if(arr.empty() || k > arr.size()) return {};
  3. if(arr.size() == k) return arr;
  4. const auto size = arr.size();
  5. short start = 0, end = size - 1, tmp = k;
  6. while(true) {
  7. // 快排分区
  8. std::swap(arr[start + ((end - start)>>1)], arr[end]);
  9. int pivot = paritition(arr, start-1, end, arr[end] );
  10. std::swap(arr[pivot], arr[end]);
  11. if(pivot == k) break; // 左分区+pivot刚好=k个。
  12. else if(pivot < k) { // 第k小的数在pivot的右侧,
  13. // 那么问题改成在[pivot+1, end]区间查找k-左区间[start, pivot]个数,因为区间[start, pivot]肯定是最小的一批数
  14. start = pivot + 1;
  15. tmp = tmp - (pivot - start + 1);
  16. }
  17. else end = pivot - 1; //第k小的数在pivot左侧,那么问题改成在start, pivot-1的区间寻找第k小的数
  18. }
  19. return vector<int>(arr.begin(), arr.begin()+k);
  20. }
  21. // 快排的partition
  22. short paritition(vector<int> &arr, short start, short end, int pivotVal){
  23. while(true){
  24. while(++start < end && arr[start] < pivotVal);
  25. while(--end > start && !(arr[end] < pivotVal));
  26. if(start < end) std::swap(arr[start], arr[end]);
  27. else break;
  28. }
  29. return start;
  30. }

方法二:最大值堆

用数列的前K个值构建一个最大值堆,然后将剩余的值挨个与堆顶的值比较,合适则替换堆顶,然后siftdown到合适位置。

  1. class Heap
  2. {
  3. public:
  4. Heap( vector<int> &arr ) : _data( arr ), _dataSize( arr.size() )
  5. {
  6. for( int i = ( arr.size() >> 1 ) - 1; i >= 0; i-- ) { siftDown( i ); }
  7. }
  8. bool empty() {return _dataSize == 0;}
  9. int top( ) {return _data[0];}
  10. void setTop( const int &val )
  11. {
  12. if( _data[0] != val )
  13. {
  14. _data[0] = val;
  15. siftDown( 0 );
  16. }
  17. }
  18. private:
  19. int leftChild( const int &index ) { return ( index << 1 ) + 1; }
  20. int rightChild( const int &index ) { return ( index << 1 ) + 2; }
  21. bool leaf( const int &index ) { return index > ( ( _dataSize >> 1 ) - 1 ); }
  22. void siftDown( const int &index )
  23. {
  24. if( !leaf( index ) )
  25. {
  26. int maxChild = leftChild( index );
  27. int rightchild = rightChild( index );
  28. if( rightchild < _dataSize && _data[maxChild] < _data[rightchild] )
  29. {
  30. maxChild = rightchild;
  31. }
  32. if( _data[index] < _data[maxChild] )
  33. {
  34. std::swap( _data[maxChild], _data[index] );
  35. siftDown( maxChild );
  36. }
  37. }
  38. }
  39. public:
  40. vector<int> &_data;
  41. int _dataSize;
  42. };
  43. vector<int> GetLeastNumbers_Solution( vector<int> input, int k )
  44. {
  45. if( k > input.size() || k <= 0 || input.empty() ) return{};
  46. if( k == static_cast<int>( input.size() ) ) { return input; }
  47. vector<int> container{ input.begin(), input.begin() + k };
  48. Heap heap( container );
  49. for( auto it = input.begin() + k; it != input.end(); it++ ) if( *it < heap.top() ) { heap.setTop( *it ); }
  50. return heap._data;
  51. };

场景二:找出第topK大的数

题目:https://leetcode-cn.com/problems/kth-largest-element-in-an-array/

方法一:分治法

  1. int findKth( vector<int> input, int n, int k )
  2. {
  3. if( k > n || k <= 0 || input.empty() || input.size() != n ) { return INT_MAX; }
  4. // 找第K大的数等于找第n-k+1小的数
  5. k = n - k + 1;
  6. int pivot = -1;
  7. int rankIdx = -1;
  8. int left = 0;
  9. int right = n - 1;
  10. while( true )
  11. {
  12. pivot = ( left + right ) >> 1;
  13. if( pivot != right ) { std::swap( input[pivot], input[right] ); }
  14. rankIdx = partition( input, left - 1, right, input[right] );
  15. if( rankIdx != right ) { std::swap( input[rankIdx], input[right] ); }
  16. if( ( rankIdx + 1 ) == k ) { return input[rankIdx]; }
  17. else if( ( rankIdx + 1 ) < k ) { left = rankIdx + 1; }
  18. else { right = rankIdx - 1; }
  19. }
  20. }
  21. // 对[start+1, end]区间的数目进行分区,其中end位置的值是pivotVal
  22. // 设返回值是k,表示位置。则有[0, ..., k-1] < [k] < [k+1, ...]
  23. int partition( vector<int> &input, int start, int end, int pivotVal )
  24. {
  25. while( true )
  26. {
  27. while( ++start != end && input[start] < pivotVal );
  28. while( --end != start && !( input[end] < pivotVal ) );
  29. if( start < end ) { std::swap( input[start], input[end] ); }
  30. else { break; }
  31. }
  32. return start;
  33. }

方法二:堆

用数列的前K个值构建一个最小值堆,然后将剩余的值挨个与堆顶的值比较,合适则替换堆顶,然后siftdown到合适位置。

  1. int findKthLargest(vector<int>& nums, int k) {
  2. if(nums.size() == 1) return nums[0];
  3. const auto size = nums.size();
  4. int idx = 0;
  5. priority_queue<int, vector<int>, greater<int>> q;
  6. // 填满k个元素。
  7. for(idx = 0; idx < k; q.push(nums[idx]), ++idx);
  8. // 筛选出K个最大的元素
  9. for(; idx < size; ++idx) {
  10. if(q.top() < nums[idx]){
  11. q.pop();
  12. q.push(nums[idx]);
  13. }
  14. }
  15. // 堆顶就是第K大的元素
  16. return q.top();
  17. }

训练题:链表排序

题目:https://leetcode-cn.com/problems/insertion-sort-list/

递归版

  1. ListNode* insertionSortList(ListNode* head) {
  2. if(!head || !head->next) return head;
  3. ListNode tmpHead, *next = head->next;
  4. tmpHead.next = head;
  5. head->next = nullptr;
  6. return insertSort(&tmpHead, next);
  7. }
  8. ListNode* insertSort(ListNode* head, ListNode* nodeToInsert)
  9. {
  10. if(!nodeToInsert) return head->next;
  11. ListNode* nextToInsert = nodeToInsert->next;
  12. ListNode* tmpNode = head;
  13. for(; tmpNode->next && tmpNode->next->val <= nodeToInsert->val; tmpNode = tmpNode->next);
  14. ListNode* fuck = tmpNode->next;
  15. tmpNode->next = nodeToInsert;
  16. nodeToInsert->next = fuck;
  17. return insertSort(head, nextToInsert);
  18. }

迭代版

  1. ListNode* insertionSortList(ListNode* head) {
  2. if(!head || !head->next) return head;
  3. ListNode tmpHead;
  4. tmpHead.next = head;
  5. ListNode *next = head->next;
  6. head->next = nullptr;
  7. ListNode* tmpNode = nullptr;
  8. for(ListNode* nodeToAdd = next; nodeToAdd;){
  9. // 找到最后一个不大于nodeToAdd的节点
  10. for(tmpNode = &tmpHead; tmpNode->next && tmpNode->next->val <= nodeToAdd->val; tmpNode = tmpNode->next);
  11. ListNode* nextTmpNode = tmpNode->next;
  12. ListNode* nextToAdd = nodeToAdd->next;
  13. tmpNode->next = nodeToAdd;
  14. nodeToAdd->next = nextTmpNode;
  15. nodeToAdd = nextToAdd;
  16. }
  17. return tmpHead.next;
  18. }

训练题:排序链表

题目:https://leetcode-cn.com/problems/sort-list/

插入排序

时间复杂度:O(n2)
空间复杂度:O(1)

  1. ListNode* sortList(ListNode* head) {
  2. if(!head || !head->next) return head;
  3. // tmpHead:临时表头,后面连接的是已排序的链表
  4. ListNode tmpHead, *next = head->next;
  5. // 初始情况,tmpHead后面是一个head链表头节点。
  6. tmpHead.next = head;
  7. head->next = nullptr;
  8. // 每次次循环将剩余链表的表头nodeToAdd插入到tmpHead链表的合适位置中。
  9. for(ListNode* nodeToAdd = next, *afterNode; nodeToAdd;){
  10. // afterNode要么是tmpHead链表的末尾,要么是最后一个不大于nodeToAdd的节点
  11. for(afterNode = &tmpHead; afterNode->next && afterNode->next->val <= nodeToAdd->val; afterNode = afterNode->next);
  12. // 将nodeToAdd插入到afterNode的后面
  13. ListNode* next_afterNode = afterNode->next;
  14. ListNode* next_nodeToAdd = nodeToAdd->next;
  15. afterNode->next = nodeToAdd;
  16. nodeToAdd->next = next_afterNode;
  17. nodeToAdd = next_nodeToAdd;
  18. }
  19. return tmpHead.next;
  20. }