数据结构与算法分析-C++描述 第7章

1.STL sort

任何排序算法的时间复杂度不会低于。
sort函数不会保证相等的元素保持原来的次序,如果有这样的需求,要用stable_sort(耗时比sort长)。
将元素按降序排列的方法:sort(v.begin(),v.end(),greater());

2.冒泡排序

输入:n,n个整数
输出:按升序排列的n个整数
从第一个数开始,每个数和后一个数比,如果它比后一个数大,则交换。这样循环一遍就会把最大的数移动到最后面,依次类推即可完成排序。

#include
using namespace std;
int a[100];
int main() {
    int n;
    cin >> n;
    int t;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n - i; j++) {
            if (a[j] > a[j + 1]) {
                t = a[j];
                a[j] = a[j + 1];
                a[j + 1] = t;
            }
        }
    }
    for (int i = 1; i <= n; i++) cout << a[i] << " ";
    return 0;
}

时间复杂度:

3.选择排序

在未排序序列中找到最小元,和第一个元素交换,直到所有元素都已排序。

#include
using namespace std;
int n;
int a[100];
int min;
int t;
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        min = i;
        for (int j = i + 1; j <= n; j++) {
            if (a[j] < a[min]) min = j;
        }
        if (min != i) {
            t = a[min];
            a[min] = a[i];
            a[i] = t;
        }
    }
    for (int i = 1; i <= n; i++) cout << a[i] << ' ';
    return 0;
}

时间复杂度:

4.插入排序

将未排序序列的每一个元素同已经排好序的序列的每一个元素比较,并把它们插入到适当的位置。

#include
using namespace std;
int n;
int a[100];
int key;
int j;
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 2; i <= n; i++) {
        key = a[i];
        for (j = i - 1; j >= 1 && key < a[j]; j--) a[j + 1] = a[j];
        a[j + 1] = key;
    }//假设1-i-1的元素已经排好了,将key插到比它小的元素后面
    for (int i = 1; i <= n; i++) cout << a[i] << ' ';
    return 0;
}

时间复杂度:

5.希尔排序

增量序列:,其中的递增序列。
分别对原序列每隔个元素做插入排列,使其每隔个元素提取出来的子序列升序,直到对每个相邻元素都做插入排列。
希尔增量:从开始,每次的间隔是前一次的一半,时间复杂度:。

#include
using namespace std;
int main() {
    int n;
    int a[100];
    int key;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int gap = n / 2; gap >= 1; gap /= 2) { //间隔从n/2到1
        for (int i = gap + 1; i <= n; i++) {  //前gap个元素相当于插入排序的第一个元素
            key = a[i];
            int j;
            for (j = i - gap; j >= 1 && a[j] > key; j -= gap) a[j + gap] = a[j]; 
            a[j + gap] = key;
        }
    }
    for (int i = 1; i <= n; i++) cout << a[i] << ' ';
    return 0;
}

Hibbard增量:,时间复杂度:

#include
#include
using namespace std;
int main() {
    int n;
    int a[100];
    int key;
    int gap;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    int maxk = log2(n + 1);
    for (int k = maxk; k >= 1;k--) { 
        gap = pow(2, k) - 1;
        for (int i = gap + 1; i <= n; i++) { 
            key = a[i];
            int j;
            for (j = i - gap; j >= 1 && a[j] > key; j -= gap) a[j + gap] = a[j]; 
            a[j + gap] = key;
        }
    }
    for (int i = 1; i <= n; i++) cout << a[i] << ' ';
    return 0;
}

6.堆排序

由第6章知,建立一个堆所用时间为,N次查找最小元所需时间,故堆排序所需时间为。
但是,堆作为数组,要占O(N)的空间,所以空间复杂度比原来大一倍。
priority_queue实现:

#include
#include
using namespace std;
priority_queue,greater > q;
int main() {
    int n;
    int key;
    int a[100];
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        q.push(a[i]);
    }
    for (int i = 1; i <= n; i++) {
        key = q.top();
        q.pop();
        a[i] = key;
    }
    for (int i = 1; i <= n; i++) cout << a[i] << ' ';
    return 0;
}

STL heap实现:
make_heap(begin,end,cmp):将数组或者向量做成堆(默认为大根堆)。
pop_heap(begin,end,cmp):将第一个元素和最后一个元素互换,并将[begin,end-1)的元素做成一个新堆。
push_heap(begin,end,cmp):要求[begin,end-1)是一个堆,把end-1加到堆里。

#include
#include
using namespace std;
int a[100];
int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    make_heap(a, a + n, greater());
    for (int i = 0; i < n; i++) {
        cout << a[0] << ' ';
        pop_heap(a, a + n - i, greater());
    }
    return 0;
}

7.归并排序

对于一个长度为N的序列,递归地将前N/2的元素和后N/2的元素分别排序,再合并起来。
只需要考虑合并两个已经排序的表:和。
分别设置两个指针,一个指向a表,一个指向b表,如果a指针指向的元素更小,则把它填入c表,并向后移动一个元素,直到一个数组的元素被使用完,将另一个数组的剩余元素复制到c表即可。
该算法最多进行次比较,故时间复杂度为。

#include
#include
using namespace std;
int a[100];
int res[100];
void merge(int lstart, int rstart, int rend) {  //将左半部分和右半部分合并
    int lend = rstart - 1;
    int temp = lstart;  //相当于指针c
    int num = rend - lstart + 1;   //元素数 
    while (lstart <= lend && rstart <= rend) {
        if (a[lstart] <= a[rstart])
            res[temp++] = a[lstart++];
        else
            res[temp++] = a[rstart++];
    }
    while (lstart <= lend) res[temp++] = a[lstart++];
    while (rstart <= rend) res[temp++] = a[rstart++];
    for (int i = 0; i < num; i++, rend--) {//rend始终没动过,从rend向前数num个数
        a[rend] = res[rend];
    }
}
void mergesort(int left, int right) {
    if (left == right) return;
    int center = (left + right) / 2;
    mergesort(left, center);
    mergesort(center + 1, right);
    merge(left, center + 1, right);
}

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    mergesort(0, n - 1);
    for (int i = 0; i < n; i++) cout << a[i] << ' ';
    return 0;
}

时间复杂度方程:


用迭代法解得,故时间复杂度为。
空间复杂度为。
归并排序比较的次数少,复制的次数多。

8.快速排序

从序列中挑出一个枢纽元a,将比a小的数放在a的前面,比a大的数放在a的后面,递归地排序a前面的数和a后面的数。

枢纽元的选取:如果选取第一个或最后一个元素作为枢纽元,若序列是反序的,则所有的数都会被放到a的一边,显然不行。好的选取是求第一个元素,中间元素和最后一个元素的中值作为枢纽元。

分割策略:先把枢纽元和最后一个元素交换,使它离开要分割的序列。设两个指针i,j分别从第一个元素和倒数第二个元素开始,当i在j的左边时,将i右移,移过那些比枢纽元小的元素,直到i所指的元素比枢纽元大;同时将j左移,直到j所指的元素比枢纽元小,交换i和j所指的元素。如果i或者j所指向的元素与枢纽元相等,直接交换。直到i和j相邻时过程终止。最后交换j所指向的元素和枢纽元。

可以证明,快排的最坏时间复杂度为,平均时间复杂度为,当N较小的时候,快排不如插入排序好。由于快排的递归性质,一定会处理N较小的情况,所以可以加一个特判:当N小于10时,用插入排序。(见书,这里如果不这样做的话,会出现未知bug)

#include
#include
using namespace std;
int a[100010];
int n;
int choose(int left, int right) { //确保最左边元素比中间元素小,中间元素比最右边元素小
    int center = (left + right) / 2;
    if (a[center] < a[left]) swap(a[left], a[center]);
    if (a[right] < a[left]) swap(a[left], a[right]);
    if (a[right] < a[center]) swap(a[center], a[right]);
    swap(a[center], a[right - 1]); //把枢纽元放在right-1,因为right比它大
    return a[right - 1];
}

void quicksort(int left, int right) {
    if (left + 10 <= right) {
        int pivot = choose(left, right);
        int i = left, j = right - 1;
        while (1) {
            if (i == n - 1 || j == 0) break;
            while (a[++i] < pivot);
            while (pivot < a[--j]);
            if (i < j) swap(a[i], a[j]);
            else break;
        }
        swap(a[i], a[right - 1]);
        quicksort(left, i - 1);
        quicksort(i + 1, right);
    }
    else {
        for (int i = left + 1; i <= right; i++) {
            int j;
            int key = a[i];
            for (j = i - 1; j >= left && a[j] > key; j--) a[j + 1] = a[j];
            a[j + 1] = key;
        }
    }
}

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    quicksort(0, n - 1);
    for (int i = 0; i < n; i++) cout << a[i] << ' ';
    return 0;
}

洛谷上的一个简化版(直接拿中间元素作为枢纽元):

#include
#include
using namespace std;
int a[100010];
int n;
void quicksort(int left, int right) {
    if (left >= right) return;
    int mid = a[(left + right) / 2];
    int i = left - 1, j = right + 1;
    while (1) {
        while (a[++i] < mid);
        while (a[--j] > mid);
        if (i <= j) swap(a[i], a[j]);
        else break;
    }
        quicksort(left, j);
        quicksort(i, right);
}

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    quicksort(0, n - 1);
    for (int i = 0; i < n; i++) cout << a[i] << ' ';
    return 0;
}

9.桶排序

设N的最大值为M,开一个大小为M的数组a[M](初始化为0),读入i就a[i]++,输出的时候有几个就输出几次。

#include
using namespace std;
int a[1000001];
int main() {
    int n;
    int temp;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> temp;
        a[temp]++;
    }
    for (int i = 1; i <= 1000001; i++) {
        while (a[i]--) {
            cout << i << ' ';
        }
    }
    return 0;
}

时间复杂度:
空间复杂度:

10.补充:快速选择

查找第k个数的最优算法,若k<=j,则答案必在前j个数内;否则,在后面的数内。

#include
using namespace std;
int a[100010];
int n;
int k;
int quickselect(int left, int right) {
    if (left == right) return a[left];
    int mid = a[(right + left) / 2];
    int i = left - 1, j = right + 1;
    while (1) {
        while (a[++i] < mid);
        while (a[--j] > mid);
        if (i <= j) swap(a[i], a[j]);
        else break;
    }
    if (k <= j) quickselect(left, j);
    else if (k > j) quickselect(i, right);
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    cin >> k;
    cout<

最坏时间复杂度:
平均时间复杂度:

你可能感兴趣的:(数据结构与算法分析-C++描述 第7章)