第八章 排序

1.插入排序

直接插入、折半插入、希尔 排序

#include <stdio.h>
void arrprint(int arr[],int n){
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
void arrprint2(int arr[],int n){
    for (int i = 1; i < n; i++) {   //浪费a[0]哨兵 实际数据从第1开始
        printf("%d ", arr[i]);
    }
    printf("\n");
}

// 直接插入排序
void insertSort(int a[], int n) {
    int i, j, temp;
    for (i = 1; i < n; i++) {// 从第二个元素开始遍历,第一个元素认为已经是有序的子数组
        if (a[i] < a[i - 1]) {// 如果当前元素小于前一个元素(前驱),则需要插入到正确的位置
            temp = a[i];  // 保存当前元素的值
            for (j = i - 1; j >= 0 && a[j] > temp; j--) // 从当前元素往前遍历,将比当前元素大的元素逐个后移
                a[j + 1] = a[j];
            a[j + 1] = temp;// 插入当前元素到正确的位置
        }
    }
}

// 折半插入排序(带哨兵即a[0]不存储实际数据)
/*
思路:将待排序元素插入到已排序序列中的正确位置,而不是像直接插入排序一样从后往前逐个比较和移动元素。
    又该方法带哨兵 实际下标从1开始存储,从第二个元素开始遍历 依次从已经排序好的序列中折半查找,
    查找完后 现在的 high后面即 low开始到i-1即有序序列的比a[i]都都大的子序列,将该子序列后移,将该元素插入到a[high+1]
*/
void inserSort2(int a[], int n) {
    int i, j, low, mid, high;
    for (i = 2; i <= n; i++) {// 从第二个元素开始,即 a[1] 到 a[n] 执行折半插入排序
        a[0] = a[i]; // 哨兵元素保存当前元素 a[i]
        low = 1; // 已排序子数组的起始位置
        high = i - 1; // 已排序子数组的结束位置

        while (low <= high) {
            mid = (low + high) / 2; // 计算中间位置
            if (a[mid] > a[0])// 如果中间位置的元素大于当前元素 a[0],将 high 移至 mid-1
                high = mid - 1;
            else
                low = mid + 1; // 否则将 low 移至 mid+1
        }
        for (j = i - 1; j >= high + 1; --j)// 将大于 a[0] 的元素逐个后移,为 a[0] 腾出插入位置
            a[j + 1] = a[j];
        a[high + 1] = a[0];// 插入 a[0] 到正确的位置
    }
}

//希尔排序
/*
默认第一个基准,下标0不存储实际数据
该代码不唯一
*/
void ShellSort(int a[],int n){ //测试案例{-1,49,38,65,97,13,27,55,4},-1不为实际值,实际长度8
    int dk,i,j;
    for(dk=n/2;dk>=1;dk=dk/2){  //增量变化
        for(i=dk+1;i<=n; i++){
            if(a[i] < a[i-dk]){         //将 a[i]插入有序增量子表
                a[0]=a[i];              //暂存在a[0]
                for(j= i-dk;j>0 && a[0]<a[j]; j-=dk)
                    a[j+dk] = a[j];     //记录后移,查找插入位置
                a[j+dk] =a[0];          //插入
            }
        }
    }
}

int main() {
    int arr[] = {12, 11, 13, 5, 6};
    int brr[] = {-1,1, 11, 3, 15, 6}; // brr[0] 为哨兵即-1不为实际有效存储值
    int crr[] = {-1,49,38,65,97,13,27,55,4};// crr[0] 为哨兵即-1不为实际有效存储值
    int n1 = sizeof(arr) / sizeof(arr[0]);
    int n2 = sizeof(brr) / sizeof(brr[0]);

    printf("直接插入排序前:");
    arrprint(arr,n1);

    insertSort(arr, n1);

    printf("排序后: ");
    arrprint(arr,n1);
    printf("\n");

    printf("折半插入排序前:");
    arrprint2(brr,n2);

    inserSort2(brr, n2);
    printf("排序后: ");
    arrprint2(brr,n2); //
    printf("\n");

    printf("希尔插入排序前:");
    arrprint2(crr,9);

    ShellSort(crr, 8);
    printf("排序后: ");
    arrprint2(crr,9); //
    printf("\n");
    return 0;
}

2.交换排序

冒泡排序、快速排序

#include <stdio.h>
#include <stdlib.h>
void print_sort(int arr[],int n){
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
void swap(int *a,int *b){
    int temp =*a;
    *a=*b;
    *b=temp;
}
//冒泡排序
void BubbleSort(int a[],int n){ //递增有序
    for(int i=0;i<n;i++){
        int flag=0;
        for(int j=n-1;j>i;j--){
            if(a[j-1] > a[j]){  //左大又小
                swap(&a[j-1],&a[j]);
                flag=1;
            }
        }
        if(flag=0)      //本次遍历没有变化,已经有序提前结束遍历
            return;
    }

}

//快速排序
//根据基准一分为二
int Partion(int a[],int low,int high){
    int key =a[low];
    while(low< high){
        while(low <high && a[high] >= key) --high;
        a[low] =a[high];
        while(low <high && a[low] <= key) ++low;
        a[high] =a[low];
    }
    a[low] =key;
    return low;
}

void QuickSort(int a[], int low,int high){
    if(low <high){
        int key =Partion(a,low,high);
        QuickSort(a,low,key-1); //将左子表 和右字表再 快排
        QuickSort(a,key+1,high);
    }
}


int main()
{
    int a[]= {49,38,65,97,76,13,27,49};
    int b[]= {49,38,65,97,76,13,27,49};
    printf("初始:");
    print_sort(a,8);

    printf("冒泡排序后:");
    BubbleSort(a,8);
    print_sort(a,8);

    printf("快速排序后:");
    QuickSort(b,0,7);
    print_sort(b,8);
    return 0;
}

3.选择排序

简单选择排序、堆排序

#include <stdio.h>
#include <stdlib.h>
void print_sort(int arr[],int n){
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
void print_sort2(int arr[],int n){
    for (int i = 1; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
void swap(int *a,int *b){
    int temp =*a;
    *a=*b;
    *b=temp;
}

//简单选择排序
void SelectSort(int a[],int n){
    for(int i=0;i<n;i++){
        int min =i; //记录最小元素位置
        for(int j=i+1;j<n;j++){
            if(a[min] >a[j])
                min =j;
        }
        if(min !=i) swap(&a[i],&a[min]);
    }

}

//堆排序
/*  (该代码为小根堆 从小到大)
建堆 排序
实际从下标1开始存储
建堆 非叶子节点从右到左顺序 依次调整直至建堆完成
排序 每次将无序序列最后一个与实际存储的第一个元素互换,然后将第一个元素视为插入在无序序列中进行调整,
每次交换的第一个元素到尾部构成有序序列
*/
void Build(int a[], int n) {
    for (int i = n / 2; i > 0; i--) {
        HeapAdjust(a, i, n);
    }
}

void HeapAdjust(int a[], int k, int n) {
    a[0] = a[k];
    int i;
    for (i = 2 * k; i <= n; i *= 2) {
        if (i < n && a[i] > a[i + 1]) // 修改这里为小根堆
            i++;
        if (a[0] <= a[i]) // 修改这里为小根堆
            break;
        else {
            a[k] = a[i];
            k = i;
        }
    }
    a[k] = a[0];
}

void HeapSort(int a[], int n) {
    Build(a, n);
    Build(a, n);
//    printf("建堆后:");
//    print_sort2(a, n + 1);
//    printf("\n");
    for (int i = n; i > 1; i--) {
//        printf("\t%d",a[1]);//这一步其实可以输出 根 就能的到想要的小根堆从小到大序列,我实际是排完序顺序打印的逆序小根堆
        swap(&a[i], &a[1]);
        HeapAdjust(a, 1, i - 1);
//        print_sort2(a, n + 1);
    }
}



int main()
{
    int a[]= {49,38,65,97,76,13,27,49};
    int b[]= {-1,87,45,88,32,17,65,53,9};   //-1不是实际存储数据
    int n1=8,n2=8;
    printf("初始:");
    print_sort(a,n1);

    printf("简单选择排序后:");
    SelectSort(a,n1);
    print_sort(a,n1);
printf("\n");
    printf("初始:");
    print_sort2(b,n2+1);
    printf("小根堆排序后:");
    HeapSort(b,n2);
    print_sort2(b,n2+1);    // 这里其实输出的逆序小根堆 从大到小,实际输出在71行一次循环输出根
    return 0;
}

小根堆插入、删除

#include <stdio.h>
#include <stdlib.h>

void print_sort2(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

void Build(int a[], int n) {
    for (int i = n / 2; i > 0; i--)
        HeapAdjust(a, i, n); // 依次调整,直至完成小根堆
}

void HeapAdjust(int a[], int k, int n) {
    a[0] = a[k];
    for (int i = 2 * k; i <= n; i *= 2) {
        if (i < n && a[i] > a[i + 1]) // 选择左右孩子小的那个
            i++;
        if (a[0] <= a[i]) // 当前节点比左右孩子节点都小满足小根堆,不需要调整
            break;
        else {
            a[k] = a[i]; // 将a[i]调整到双亲节点上
            k = i; // 修改k值,以便继续向下筛选
        }
    }
    a[k] = a[0]; // 将筛选值插入最终位置
}

void HeapSort(int a[], int n) {
    Build(a, n); // 初始建堆
    for (int i = n; i > 1; i--) {
        swap(&a[i], &a[1]); // 将堆顶 与堆尾互换,尾部为有序有序
        HeapAdjust(a, 1, i - 1); // 交换后,调整
    }
}

// 插入
void Insert(int a[], int *n, int value) {
    (*n)++;
    a[*n] = value;
    int i = *n;
    while (i > 1 && a[i] < a[i / 2]) {
        swap(&a[i], &a[i / 2]);
        i /= 2;
    }
}
int DeleteMin(int a[], int *n) {
    int min = a[1];
    a[1] = a[*n];
    (*n)--;
    HeapAdjust(a, 1, *n);
    return min;
}


int main() {
    int b[] = {-1, 87, 45, 78, 32, 17, 65, 53, 9}; // -1不是实际存储数据
    int n2 = 8;
    printf("初始: ");
    print_sort2(b, n2 + 1);

    Build(b, n2); // 构建小根堆
    printf("小根堆构建后: ");
    print_sort2(b, n2 + 1);

    Insert(b, &n2, 20);
    printf("插入后的小根堆: ");
    print_sort2(b, n2 + 1);

    int min = DeleteMin(b, &n2);
    printf("删除的最小元素: %d\n", min);
    printf("删除后的小根堆: ");
    print_sort2(b, n2 + 1);
    return 0;
}

4.归并排序、基数排序

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

void print_sort(int a[],int n){
    for (int i = 0; i < n; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");
}
//归并排序
int b[8]; // 全局变量 辅助变量用于复制a
void Merge(int a[], int low, int mid, int high){
    int i,j,k;
    for(k=low; k<=high;k++)
        b[k] =a[k];             //将 a元素 复制到 b
//    print_sort(b,8);    //测试
    for(i=low,j =mid+1,k=low;i<=mid && j<=high;k++){  //将复制后b中元素 按照新规律放回a
        if(b[i] <= b[j])        //比较b 左右两端中元素
            a[k] = b[i++];      //将小的放回a中
        else
            a[k] = b[j++];
    }
    while(i<=mid)   a[k++]=b[i++];  //当其中一个分区空了剩下的直接按顺序放回
    while(j<=high)   a[k++]=b[j++];
}

void MergeSort(int a[],int low,int high){
    if(low< high){
        int mid =(low+high)/2;      //对半分两个 子序列
        MergeSort(a,low,mid);       //递归左子序列
        MergeSort(a,mid+1,high);
        Merge(a,low,mid,high);      //归并
    }
}

int main()
{
    int a[]= {49,38,65,97,76,13,27,49}; //长度8
    int n = sizeof(a) / sizeof(a[0]);
    printf("归并排序前序列:");
    print_sort(a,n);
    MergeSort(a,0,n-1);
    print_sort(a,n);
    return 0;
}

5.习题

3.2 双向冒泡排序

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

void swap(int* a,int* b){
    int temp=*a;
    *a=*b;
    *b=temp;
}
void BubbleSort(int a[],int n){
    int low=0,high=n-1,i;
    bool flag =true;
    while(low< high && flag){
        flag =false;            //当flag一次循环没有变化,说明当前以及有序,结束条件
        for(i=low;i<high;i++){  //从左到右 最大值到尾
            if(a[i]>a[i+1]){
                swap(&a[i],&a[i+1]);
                flag=true;
            }
        }
        high--;
        for(i=high;i>low;i--){  //从右到左 最小值到头
            if(a[i] < a[i-1]){
                swap(&a[i],&a[i-1]);
                flag=true;
            }
        }
        low++;
    }
}
int main()
{
    int a[]={7,1,2,9,4,3};
    for(int i=0;i<6;i++)
        printf("%d ",a[i]);
    printf("\n");
    BubbleSort(a,6);
    printf("双向冒泡后:");
    for(int i=0;i<6;i++)
        printf("%d ",a[i]);
    return 0;
}

3.3 奇偶分开

#include <stdio.h>
#include <stdlib.h>

void swap(int *a,int *b){
    int temp =*a;
    *a =*b;
    *b =temp;
}

void Sort(int a[],int n){
    int i=0,j=n-1;
    while(i<j){
        while(i<j && a[i]%2==1) i++;    //从左到右 遍历元素为偶数
        while(i<j && a[j]%2==0) j--;    //从右到左 遍历元素为奇数
        if(i<j)
            swap(&a[i++],&a[j--]);
    }
}
int main()
{
    int a[]={7,1,2,9,4,3};
    for(int i=0;i<6;i++)
        printf("%d ",a[i]);
    printf("\n");
    Sort(a,6);
    printf("奇偶排序后:");
    for(int i=0;i<6;i++)
        printf("%d ",a[i]);
    return 0;
    return 0;
}

3.7 分区 最大区最小区的和差最大 长度差最小

void Sort(int a[],int n){
    int key,low=0,low1=0,high=n-1,high1=n-1,flag=1,k=n/2,i; //注分区刚好一半,小的一半左 大的分区右
    int s1=0,s2=0;
    while(flag){
        key= a[low];
        while(low<high){    // 快排
            while(low<high && a[high] >= key) --high;
            if(low !=high) a[low] =a[high];
            while(low<high && a[low] <= key) ++low;
            if(low!=high) a[high]=a[low];
        }
        a[low] =key;
        if(low == k-1)      //将基准趋于中间
            flag=0;
        else{
                if(low < k-1){
                    low1 = ++low;
                    high=high1;
                }
                else{
                    high1 =--high;
                    low =low1;
                }
            }
    }
    for(i=0;i<k;i++) s1+=a[i];
    for(i=k;i<n;i++) s2+=a[i];
    return s2-s1;
}

4.4 单链表简单选择排序

#include <stdio.h>
#include <stdlib.h>

typedef struct node {
    int data;
    struct node* next;
} node, *linklist;

linklist init_list() {
    linklist lt = (linklist)malloc(sizeof(node)); // 创建头节点
    if (lt == NULL) {
        printf("内存分配失败!\n");
        return NULL;
    }
    lt->next = NULL;
    return lt;
}

void print_list(linklist lt) {
    lt = lt->next;
    if (lt == NULL) {
        printf("链表为空!\n");
        return;
    }
    printf("链表中的元素:");
    while (lt) {
        printf("%d\t", lt->data);
        lt = lt->next;
    }
    printf("\n");
}

linklist head_insert(linklist lt) {
    int n = 0, e = 0;
    printf("请输入要插入的元素个数:");
    scanf("%d", &n);
    printf("请输入要插入的元素:");
    for (int i = 0; i < n; i++) {
        node* s = (node*)malloc(sizeof(node));
        scanf("%d", &e);
        s->data = e;
        s->next = lt->next;
        lt->next = s;
    }
    return lt; // 返回更新后的链表
}

void SelectSort(linklist lt) {
    node *p, *q, *min, *temp;

    p = lt->next;  // p 指向当前待排序的元素
    while (p != NULL) {
        min = p;  // min 用于记录当前轮次中的最小元素
        q = p->next;  // q 用于遍历剩余未排序的元素
        while (q != NULL) {
            // 如果找到一个比当前最小元素更小的元素,更新 min
            if (q->data < min->data) {
                min = q;
            }
            q = q->next;
        }
        if (min != p) {
            // 如果 min 不等于 p,表示找到了更小的元素,进行元素交换
            temp = p->data;
            p->data = min->data;
            min->data = temp;
        }
        p = p->next;  // 移动 p 到下一个待排序的元素
    }
}
int main() {
    linklist lt = init_list();  // 创建带头节点的链表
    lt = head_insert(lt);       // 更新插入元素后的链表
    print_list(lt);

    printf("简单选择排序后的链表:");
    SelectSort(lt);             // 对链表中的元素进行排序
    print_list(lt);
    return 0;
}

4.5 判断是否为小根堆

#include <stdio.h>
#include <stdlib.h>

void print_sort2(int arr[],int n){
    for (int i = 1; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
void swap(int *a,int *b){
    int temp =*a;
    *a=*b;
    *b=temp;
}
void Build(int a[],int n){  //n为长度
    for(int i=n/2;i>0;i--)
        HeapAdjust(a,i,n);    //依次调整,直至完成大根堆
}

void HeapAdjust(int a[],int k,int n){
    a[0] = a[k];
    for(int i=2*k; i<= n; i*=2){
        if(i<n && a[i] <a[i+1]) i++;    //选择左右孩子大的那个
        if(a[0] >= a[i])    break;      //当前节点比 左右孩子节点都大满足大根堆,不需要调整
        else{
            a[k] =a[i];     //将a[i]调整到双亲节点上
            k=i;            //修改k值,以便继续向下筛选
        }
    }
    a[k] = a[0];    //将筛选值插入最终位置
}

void HeapSort(int a[],int n){
    Build(a,n); //初始建堆
    for(int i=n;i>1;i--){
        swap(&a[i],&a[1]);  //将堆顶 与堆尾互换,尾部为有序有序
        HeapAdjust(a,1,i-1);    //交换后,调整
    }
}

int minHeap(int a[],int n){
    if(n %2 ==0){
        if(a[n/2] > a [n])
            return 0;
        for (int i = n / 2 - 1; i >= 1; i--) { // 修正此处的循环条件
            if (a[i] > a[2 * i] || a[i] > a[2 * i + 1])
                return 0;
        }
    }
    else{
            for (int i = n / 2 - 1; i >= 1; i--) { // 修正此处的循环条件
                if (a[i] > a[2 * i] || a[i] > a[2 * i + 1])
                    return 0;
            }
        }
    printf("是小根堆\n");
    return 1;
}
int main()
{
    int b[]= {-1,87,45,78,32,17,65,53,9};   //-1不是实际存储数据
    int n2=8;
    printf("初始:");
    print_sort2(b,n2+1);
    minHeap(b,n2);

    printf("小根堆排序后:");
    HeapSort(b,n2);
    print_sort2(b,n2+1);
    minHeap(b,n2);

    b[1]=18;
    print_sort2(b,n2+1);
    minHeap(b,n2);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值