一个菜鸡从新开始复习算法和数据结构。
(以前的自己真实好聪明啥都会)
冒泡排序:从前往后依次比较,每次把最大的数排到后面。稳定
int i,j,tmp; for(i=n-1; i>=0; i--){//长度 for( j=0;j<i; j++){ if (x[j]>x[j+1]){ tmp = x[j]; x[j] = x[j+1]; x[j+1] = tmp; } } }
选择排序与冒泡排序类似,不断选择没排序的最小元素放到排序序列的起点。不稳定。
每次和前n-1的数比较插入合适位置,空间复杂度O(1)
int i,j,tmp; for(i=1; i<n; i++){ tmp = x[i]; for(j = i-1; j>=0 && x[j]>tmp; j--){ arr[j+1] = x[j]; } arr[j]=tmp; }
复杂度比插入低,没快排/归并快。
另一方面,复杂度和取得gap有关。
每隔gap进行插入排序,gap = n/2, n/4, n/8…
int gap,i,j,tmp; for( gap = n/2; gap>0; gap/=2){ for (i=gap; i<n; i++){ tmp = x[i]; for (j = i-gap; j>=0 && x[j]>tmp; j-=gap){ arr[j+gap]=arr[j]; } arr[j+gap]=tmp; } }
空间复杂度O(n)
合并两个已排序数组,自顶向下递归
void merge_sort(int source[], int tmp[], int sid, int eid){ int mid; if( sid < eid){ mid = sid + (eid-sid)/2; merge_sort(source, tmp, sid, mid); merge_sort(source, tmp, mid+1, eid); merge(source, tmp, sid, mid, eid); } } void merge(int source[], int tmp[], int sid, int mid, int eid){ int i = sid, j = mid, k = sid; while( i!= mid+1 && j!= eid+1){ if(source[i] < source[j]){ tmp[k++]=source[i++]; } else{ tmp[k++]=source[j++]; } } while( i!= mid+1 ){ tmp[k++]=source[i++]; } while( j!= eid+1 ){ tmp[k++]=source[j++]; } }
空间复杂度O(logn)
每次对一个数找到确定的位置,前面的都小于它,后面的都大于它。
int partition(int x[], int sid, int eid){ int num = x[sid]; while( sid < eid ){ while( sid < eid && A[eid] >= num){ --eid; } A[sid] = A[eid]; while(sid < eid && A[sid] <= num){ ++sid; } A[eid] = A[sid]; } A[sid] = num; return sid; } void quick_sort(int x[], int sid, int eid){ if (sid < eid){ int mid = partition(x, sid, eid); quick_sort(x, sid, mid-1); quick_sort(x, mid+1, eid); } }
不需要对整个数组进行排序,复杂度O(n)。
leetcode 215(https://leetcode-cn.com/problems/kth-largest-element-in-an-array)
class Solution { public: int findKthLargest(vector<int>& nums, int k) { int rid = nums.size()-k; qsort(nums, rid, 0, nums.size()-1 ); return nums[rid]; } void qsort(vector<int>&nums, int k, int sid, int eid){ int now = pos(nums, sid, eid); if (now == k){ return; } else if( now < k ){ qsort(nums, k, now+1, eid); } else{ qsort(nums, k, sid, now-1); } } int pos(vector<int>& nums, int sid, int eid){ int num = nums[sid]; while( sid < eid ){ while( sid < eid && nums[eid] >= num){ --eid; } nums[sid]=nums[eid]; while( sid < eid && nums[sid] <= num){ ++sid; } nums[eid]=nums[sid]; } nums[sid] = num; return sid; } };
void heap_sort(int x[], int n){ for(int i=n/2-1; i>=0; i--){ max_heap(x, i, n-1); } for(int i=n-1; i>=0; i--){ swap(x[0],x[i]); max_heap(x, 0, i-1); } } void max_heap(int x[], int sid, int eid){ int fa = sid; int son = sid*2+1; while( son <= eid ){ if( son+1<=eid && x[son] < x[son+1]){ ++son; } if(x[fa]>=x[son]){ return; } else{ swap(x[son],x[fa]); fa = son; son = fa*2+1; } } }
精髓都在堆排序里。==
堆底插入,堆顶取出。以数组形式存储的二叉树。
基本存储要求:每个父结点大于它的两个子节点。
分为两个操作:shift up/ shift down.
shift up即构造堆的过程,从n/2往前依次进行调整,保证整体结构:
for(int i=n/2-1; i>=0; i--){ max_heap(x, i, n-1); }
shift down是将顶点的最大元素取出后,和末位元素调换;将顶点往下交换到正确位置,以保证整体结构仍然满足堆的定义,此时顶点依然是最大的元素。
swap(x[0],x[i]); max_heap(x, 0, i-1);
堆排序:将上述过程合在一块就是堆排序过程。优化时考虑建立索引。