数据结构第八章排序c语言实现代码

第八章:排序

存储结构:

//记录序列一顺序表存储
#define MAXSIEZ 20	//设记录不超过20个
typedef int KeyType;	//设关键字为整数型
//定义每个记录(元素)的结构
Typedef struct{
    KeyType key;	//关键字
    ingoType otherinfo;		//其他数据
}
//定义顺序表的结构
Typedef struct{
    RedType r[MAXSIZE +1];		//存储顺序表的向量,r[0]一般作为哨兵或者缓冲区
    int length;		//顺序表的长度
}

1、直接插入排序法:

思想:把序列分成两个部分,有序在前,无序在后,i指向无序的第一个值,j=i-1指向有序的最后一个值,把第一个无序的元素赋值给哨兵,让他和有序中的值比较大小,直到找到比他小的值就结束,开始插入,把比他大的值都后移一位L.r[j+1] = L.r[j]

image-20231108164051611

//先把未排序的值分支给x
x=a[i];
//从有序序列的最后一个元素开始比较,如果比较之后比关键字大则往后移动
for (j=i-1; j>=0 && x<a[j], j--)	//j到最前面和小于关键字就结束
    a[j+1] = a[j];
//当找到比关键字小的值后就把第i位置的元素插入到其后面的位置
a[j+1]=x;
//这里为什么是j+1,因为j需要走到比x小的值那里比较,发现比关键字小则循环结束,把他插入到第j元素后面,把第原来j+1的值后移

加上哨兵的算法:

在这里插入图片描述

void InsertSort(SqList &L){
    int i,j;
    for(i=2; i<L.length; ++i){
        if (L.r[i].key < L.r[i-1].key){	//比较序号2和1的值大小
            //若第i的值小于前面第i-1的值,则插入
            L.r[0] = L.r[i];	//复制作为哨兵
            //后移
            //如果j的值比哨兵大,则哨兵继续向前比较直到找到比他小的值结束
            for(j=i-1; L.r[0].key < L.r[j].key; --j){
                L.r[j+1] = L.r[j]	//把有序序列中的元素向后移动
            }//for
            L.r[j+1] = L.r[0];		//插入到正确的位置
        }//if
    }//for
}
算法实现:
#include <stdio.h>
//打印函数
void print_array(int arr[], int n){
	for (int i=0; i<n; i++){
		printf("%d ",arr[i]);
		}
		printf("\n");
}

//直接插入排序
void insertion(int arr[], int len){
	int i,j,key;
	for (i=1; i<len; i++){
		key = arr[i];	//哨兵
		j=i-1;			
		while (j>=0 && arr[j]>key){
			arr[j+1] = arr[j];	//移动
			j--;		//继续往前比较
		}//while
		arr[j+1] = key;	//插入
		print_array(arr,len);
	}//for
}
//主函数
int main(){
	int arr[]={5,4,8,6,3,9,11,75};
	insertion(arr, 8);
}

2、折半插入排序法:

思想:和直接插入差不多,区别在于第i元素和有序元素比较的方法是用折半插入

image-20231116214710313

void BInserSort(SqList &L){
	for(i=2; i<=L.length; ++i){
        L.r[0] = L.r[i];	//哨兵
        
        //初始化折半
        low = 1;
        high = i-1;
        //开始折半,找到插入位置
        while(low<=high){
            mid = (low + high)/2;
            if (L.r[0].key < L.r[mid].key)		//哨兵和中间点比较
                high = mid-1;
            else low = mid + 1;		//看大小赋值low或high
        }//while,当low大于high时循环结束,则high+1就是要插入的位置
        
        //找到插入位置后开始移动元素
        for(j=i-1; j>=high+1; j--)
            L.r[j+1] = L.r[j];
        L.r[high+1] = L.r[0];
    }//for
}

3、希尔排序:

思想:

间隔不是1的插入排序,先把一段数据分成五个五个间隔的排序,然后每段中的每个来比较,然后再把小的数值放到前面,五个分段的弄完之后,就再三个分段来搞,最后间隔一个来搞,最后到排序完成

image-20231108215043742

image-20231108215636494

主程序:

void ShellSort(Sqlist &L, int dlta[], int t){
    for (k=0; k<t; ++k)
        ShellInsert(L, dlta[k]);	//设置好增量k的值
}

排序过程

void ShellInsert(SqList &L, int dk){
    //对顺序表L进行一趟增量为dk的Shell排序,dk为步长
    for(i=dk+1; i<=L.length; ++i){
        if (r[i].key < r[i-dk].key){
            r[0] = r[i]		//哨兵

            //移动
            for(j=i-dk; j>0 && (r[0].key < r[j].key); j=j-dk)	//j的变化就是减去一个间隔的值
                r[j+dk] = r[j];		//移动元素也是要移动一个间隔
            r[j+dk] = r[0];
        }//if
    }//for
}
void shell_sort(int arr[], int n){
    int i, j, inc, key;
    for (inc=n/2; inc>0; inc/=2){
        //每一趟常用插入排序
    for (i=inc; i<n; i++){
        key = arr[i];	//哨兵
        for (j=i; j>=inc && key<arr[j-inc]; j-=inc)
            arr[j]=arr[j-inc];	//交换
        arr[j]=key;
    }
    print_arr(arr, n);
  }
}

4、冒泡排序:

image-20231116220721792

按照序号两两比较,逆序则交换,从头开始一个元素进行比较,然后看大小移动

image-20231116220625539

image-20231116220753558

void bubble_sort(SqList &L){//冒泡排序法
	int i,j,m;
    RedType x;	//创建一个临时存储空一共要走的趟数=元素个数-1间来存放交换时的元素值
    for(m=1; m<n-1; m++){	//一共要走的趟数=元素个数-1
        for(i=1; i<n-m; i++)	//每一趟需要比较的次数=元素个数-趟数
            //前后对比,把大的值交换后面
            if(L.r[j].key > L.r[j+1].key){
                x=L.r[j]; L.r[j]=L.r[j+1]; L.r[j+1]=x;
                //交换方法:先把前面的值赋值给x然后把后面的值赋值给前面已经空的地址,然后再把x中存放的前面的值赋值到后面的地址中
            }
    }//for
}

改进:设置一个flag,如果后面是有序的就会减少比较次数

void bubble_sort(SqList &L){
    int m,i,j,flag=1;	//设置一个flag值
	RedType x;
    for (m=1; m<n-1; m++){
        flag=0;		//循环的条件加多了一个flag,先把flag值置零
        for (i=1; i<n-m; i++)
            if (L.r[j].key > L.r[j+1].key)
            {
                flag=1;		//如果是逆转则把flag置一
                x=L.r[j];
                L.r[j]=L.r[j+1];
                L.r[j+1]=x;
            }
    }
}

image-20231127162340951

void print_arr(int arr[], int length, int flag){
    //输出每个元素的值
    for (int i=0; i<length; i++){
        printf("%d ", arr[i]);
    }
    //看是否有转换并输出
    printf("%s\n", flag?"flag":"no flag");
}

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

void bubble_sort(int arr[], int length){
    //比较的趟数,当只有一个元素就不需要比较
    for (int i=length; i>1; i--){
        int flag = 0;	//是否发生转换
        //从第一个开始比较
        //每趟需要比较次数逐渐减少
        for (int j=1; j<i; j++){
            if (arr[j-1]>arr[j]){
                swap(&arr[j-1], &arr[j]);
                flag=1;
            }
        }
        print_arr(arr, length, flag);
        if (!flag)
            break;
    }
}

int main(){
    int arr[]={9,3,5,1,7};
    print_arr(arr, 5, 0);
    bubble_sort(arr, 5);
    return 0;
}

5、快速排序:

image-20231116222225459

image-20231109184509458

改进思路:

由于需要空间太大,则改进为另外一种方法

把序号1元素赋值给序号0,然后定义序号2元素为low,最后一个元素为high,由于序号1空了,则需要从后面找一个比原序号2小的值填到空的序号1的位置,如果后面的值移动到了前面出现了空位,则需要从前面开始找

image-20231116222512193

//找中心点的算法
int Partition(SqList &L, int low, int high){
    //一开始
    L.r[0] = L.r[low];	//哨兵
    pivotkey = L.r[low].key;//最开始中心点的值为low的值
    while (low<high){//low和high重叠的时候就结束
        //先从high这边开始查找有没有比中心点小的值把他移动到low这里来
        while (low<high && L.r[high].key >= pivotkey) 
            --high;//没找到比中心点小的值,high自减,继续循环
        	L.r[low] = L.r[high];
        //当high这边空出时,就要从low这边开始查找比中心点大的时移动到high
        while (low<high && L.r[low].key <= pivotkey)
            ++low;
        	L.r[high] = L.r[low];
    }//while
     //当上面两个循环都结束后,把序号0的元素的值赋值给low输出作为中心点
    L.r[low] = L.r[0];
    return low;
}
//直接排序算法实现
void main(){
    QSort(L,1,L.length);	//递归算法实现排序
}

//使用递归算法对顺序表排序
void QSort(Sqlist &L,int low, int high){
    if (low<high){//分段的顺序表的元素个数大于1,low和high不重叠
        pivotloc = Partition(L,low,high);
        //将L.r[low...high]一分为二,pivotloc为中心点元素排序的位置
        QSort(L,low,pivotloc-1);	//对元素较小的子表递归排序
        QSort(L,pivotoc+1,high);	//对元素较大的子表递归排序
    }//if
}

6、简单选择排序:

**思想:**就是从一个表n个元素中每次找到一个最大或者最小的值放到最后或者最前,然后再从n-1个元素中继续之前的排序

从第一个开始逐个对比找出比他小的值,相互交换,有n个元素则需要排序n-1趟排序

image-20231109222056231

image-20231109222009443

void SelectSort(SqList &K){
    for(i=1;i<L.length;i++){//从第一个元素开始和后面的元素进行比较
        k=i;	//先把第一个元素赋值给k
        for(j=i+1;j<L.length;j++)	//从第i的后一个元素开始比较
            if (L.r[j].key < L.r[k].key)	//k大于j的值
                k=j;	//记录最小值给k
        if (k!=i)
            L.r[i]交换L.r[k];
    }
}
//交换
void swap(int *a, int *b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}
void selection_sort(int arr[], int n){
    int i,j,min;
    
    for (i=0;i<n;i++){
        min=i;		//把第一个元素复制给哨兵
        for (j=i+1;j<n;j++){
            if (arr[j]<arr[min])	//i和i+1的元素比较大小
                min=j;		//把更小的序号赋值给min
        }//for_2
        //交换最小值
        swap(&arr[min], &arr[i]);
        print_arr(arr,n);
    }//for_1
}//selection

7、堆排序:

  1. 思想:分为大根堆(大值为根)和小根堆(小值为根),用二叉树表示,父结点的值一定大于或者小于左右子树的值
  2. 调整:在小根堆里面,把一个小根堆的堆顶元素输出到顺序表中,然后把序号最后的结点放在根结点上,然后比较其和左右子树的值的大小,如果大于子树的值则调下去,然后重复,直到形成有序序列

image-20231117221430207

image-20231109225935423

//堆的维护:
void heapify(int arr[], int n, int i){
    //已知父结点和左右子结点的序号关系
	int largest = i;
    int lson = i*2+1;
    int rson = i*2+2;
    
    //大根堆
    //比较父结点和左右结点的大小
    if (lson<n && arr[largest]<arr[lson])
        largest = lson;		//左子树大,则向上移动
    if (rson<n && arr[largest]<arr[rson])
        largest = rson;		//右子树大,则向上移动
    //前面把largst赋值为i,当largest不为i说明他比自己的左右子树小,所以交换元素值
    if (largest !=i ){
        swap(&arr[largest], &arr[i]);
        //交换后再判断是否满足大根堆的性质,不满足则递归
        heapify(arr, n, largest);
    }
}

从最后一个叶子结点n父结点n/2开始,n/2结点和其子树比较大小,然后交换,在到第n/2-1个结点和其子树比较,一直到整棵树的根结点处,然后再从上往下比较交换,直到最小或者最大值到二叉树的根就输出这个根,然后把最后一个结点放到根的位置上

image-20231118100701623

//堆排序
void heap_sort(int arr[], int n){
    //建堆
    int i;
    //n不是下标,是数组长度,转换成下标还要减1
    for (i=n/2-1; i>=0; i--)	//n/2-1是开始处理的第一个元素的父结点的下标
        heapify(arr, n, i);
    //排序
    //原本的元素有n个,那么剩余元素有n-1个需要比较
    for (i=n-1; i>0; i--){ //排序从最后一个元素开始
        swap(&arr[i], &arr[0]);		//最后一个元素和堆顶元素进行交换
        heapify(arr, i, 0);		//再进行一次调整
    }
}

8、归并排序:

//合并
void merge(int arr[], int tempArr[], int left, int mid, int right){
    //标记左半区第一个未排序的元素
    int l_pos=left;
    //标记左半区第一个未排序的元素
    int r_pos=mid+1;
    //临时数组元素的下标
    int pos=left;
    
    //合并
    //判断左右第一个值谁大谁小,把小的赋值给新数组
    while (l_pos<=mid && r_pos<right)
    {
        if (arr[l_pos] < arr[r_pos])	//左半区第一个元素更小
            tempArr[pos++] = arr[l_pos++];	//把小的元素复制给临时数组,然后各自后移一位
        else (arr[l_pos] > arr[r_pos])	//右半区第一个元素更小
            tempArr[pos++] = arr[r_pos++];
    }//while
    
    //如果合并完左边的元素后,右边还有剩余元素,同时右边是有序的,可以直接放到整个有序数组中
    //合并左半区剩余的元素
    while (l_pos <= mid)
        tempArr[pos++] = arr[l_pos++];
    //合并右半区剩余的元素
    while (r_pos <= right)
        tempArr[pos++] = arr[r_pos++];
    //把临时数组中合并后的元素复制回原来的数组
	while (left <= right)
    {
        arr[left]=tempArr[left];
        left++;
    } 
}   
    
//归并排序
void msort(int arr[],int tempArr[], int left, int right){
    //如果只有一个元素,那么不需要继续划分,直接就
    if (left<right){
    //找中间点
    int mid = (left + right)/2;
        
    //递归划分左半区
    msort(arr, tempArr, left, mid);
    //递归划分右半区
    msort(arr, tempArr, mid+1, right);
        
    //合并已经排序好的部分
    merge(arr, tempArr, left, mid, right);
    }//if
}


void merge_sort(int arr[], int n){
    //辅助数组分配空间
    int *tempArr = (int*)malloc(n*sizeof(int));
    //如果分配成功
    if (tempArr)
    {
        //进行排序
        msort(arr, tempArr, 0, n-1);
        free(tempArr);
    }
    else
    {
        printf("error");
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值