一个菜鸡从新开始复习算法和数据结构。
(以前的自己真实好聪明啥都会)
文章目录
排序
冒泡排序和选择排序O(n^2)
冒泡排序:从前往后依次比较,每次把最大的数排到后面。稳定
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;
}
}
}
选择排序与冒泡排序类似,不断选择没排序的最小元素放到排序序列的起点。不稳定。
插入排序O(n^2)
每次和前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(nlogn), O(n)
空间复杂度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(nlogn), O(logn)
空间复杂度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);
}
}
取数组中第 n 大的元素
不需要对整个数组进行排序,复杂度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;
}
};
堆排序O(nlogn), O(1)
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);
堆排序:将上述过程合在一块就是堆排序过程。优化时考虑建立索引。