【算法和数据结构】排序和堆

一个菜鸡从新开始复习算法和数据结构。
(以前的自己真实好聪明啥都会)

文章目录

排序

冒泡排序和选择排序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);

堆排序:将上述过程合在一块就是堆排序过程。优化时考虑建立索引。

上一篇:matplotlib添加子图


下一篇:P1045 [NOIP2003 普及组] 麦森数