三大类六种排序算法-C

1-排序简单介绍

排序:将无序键重新排列,使之有序
键集:键的集合,这里默认待排序的数据都是唯一的,即不存在重复数据,这是一种理想情况,通常不作为重点
有序:升序和降序
内排序和外排序:内存数据排序和外存数据排序(通常外排序是通过调入内存中实现排序)
稳定性:假设某次待排序的数据中,存在相同数据,排序前其物理顺序与排序后物理顺序不变,即对于所有数据,任意排序,只要依然用这种算法,其顺序是不会改变的

三大类六种排序算法:
(1):直接插入排序,希尔排序
(2):直接选择排序,堆排序(利用完全二叉树)
(3):直接交换排序(冒泡排序),快速排序
在这里插入图片描述

对每一种排序算法,通常需要从下面几个方面研究:
(1):算法
(2):C代码的实现
(3):算法的时间复杂度
(4):算法的极端效率分析(最优情况,最劣情况)
(5):稳定性分析

2-直接插入排序
	for (i = 1; i < count; i++) {
    
    
		tmp = data[i];
		for (j = 0; j < i && tmp >= data[j]; j++) {
    
    
		}
		for (t = i; t > j; t--) {
    
    
			data[t] = data[t - 1];
			//在j ~ i-1内的所有元素从后向前依次后移一位
		}
		data[j] = tmp;
	}	
}

在这里插入图片描述

3- 直接选择排序
for (i = 0; i < count - 1; i++) {
    
    
		minIndex = i;
		for (j = i; j < count; j++) {
    
    
			if (data[j] < data[minIndex]) {
    
    
				minIndex = j;
			}
		}
		if (minIndex != i) {
    
    
			tmp = data[i];
			data[i] = data[minIndex];
			data[minIndex] = tmp;
		}
	}	

在这里插入图片描述

4-直接交换排序(冒泡排序)
int hasSwap = 1;
for (i = 0; hasSwap && i < count - 1; i++) {
    
    
		hasSwap = 0;
		for (j = 0; j < count - i - 1; j++) {
    
    
			if (data[j] > data[j+1]) {
    
    
				tmp = data[j];
				data[j] = data[j+1];
				data[j+1] = tmp;
				hasSwap = 1;
			}
		}
	}	

对一组待排序数,每次相邻的两个数比较,这两个数中,将数值大的放在后面,数值小的交换到前面,第一轮交换结束后,最后一个数就是待排序数列中的最大数,第二轮后,倒数第二个数就是第二大的数,直到只剩下一个数时停止

5- 希尔排序(改良的直接插入排序)

void shellOneSort(int *data, int count, int start, int step) {
    
    
	int i;
	int j;
	int t;
	int tmp;

	for(i = start + step; i < count; i+=step) {
    
     
	//每次将下标为start+=step的元素放入一组
		tmp = data[i];
		for(j = start; j < i && tmp >= data[j]; j += step){
    
     
		//本组开始进行直接插入排序
			;  //遍历依次找到合适位置
		} 
		for(t = i; t > j; t -= step) {
    
     
		//要交换的两个值中间间距为step
			data[t] = data[t - step];  
		//则这两个值中间的元素依次后移step位
		}
		data[j] = tmp;
	}
}


void shellSort(int *data, int count) {
    
    
	int start;
	int step;

	for(step = count/2; step; step/2) {
    
     //计算step,step不能小于0
		for(start = 0; start < step; start++) {
    
      
		//控制分组的次数,step为几,就有几个start
			shellOneSort(data, count, start, step); 
			//若干个start,每次对一组直插排序
		}
	}
}

在这里插入图片描述
直接插入排序是步长为1的希尔排序

6-堆排序(改良的直接选择排序)

堆排序基于直接选择排序,使用了完全二叉树的概念
基于 大根堆(升序) 小根堆(降序) 完成排序

大根堆:一颗完全二叉树,它的任意一个非子叶结点的值,都比其左右子树中的最大值都大(小根堆参考此概念)
事实上,任意一个连续的,有n个元素的一维数组,都可以看成一颗完全二叉树,且存在如下特点:(在下标为0的前提下)
若某个非子叶结点的下标为 i:则其左孩子下标为 2 * i + 1,
右孩子下标为 2 * i + 2,下标最大的非子叶结点下标值为 n / 2 - 1
在这里插入图片描述

用大根堆排序的过程:
1-将数组内数据依次插入完全二叉树中,建立一个大根堆
2-将大根堆进行调整,直到满足:父结点的值一定大于左右孩子的值
3-将最后一个子叶结点的数与最大值(最上面的根结点)互换位置,并将最大数所在的子叶结点从大根堆中取出
4-取出后,继续调整大根堆,执行第二步,让大数先上浮,小数下沉
调整结束后,再执行第三步,直到排序完成

初始化一个大根堆
在这里插入图片描述
用大根堆完成堆排序
在这里插入图片描述

void adjustHeap(int *data,int count, int root) {
    
    

	int left;
	int right;
	int maxNode;
	int tmp;


	while(root <= count/2 - 1) {
    
    
	// count/2-1是编号最大的非子叶结点,
	//包括它和比它大的都是非子叶结点
		left = 2*root - 1; 
		right = left + 1;
		//该非子叶节点的左右孩子下标

		maxNode = right >= count ? left
		          :(data[left] > data[right]
		          ? left : right);
		//比较某非子叶结点左右孩子大小,右孩子下标不可能大于等于count
		//就是左孩子为最大,或比较值,输出值较大的下标
		maxNode = data[root] > data[maxNode]
		           ? root : maxNode;
		//比较该非子叶节点的值和maxNode,确定新的maxNode
		if(maxNode == root) {
    
    
			return;
		//非子叶结点的值为最大值,则什么也不做
		}
		tmp = data[root];
		data[root] = data[maxNode];
		data[maxNode] = tmp;
		//若非子叶点值较小,则交换值

		root = maxNode;
		//令新的非子叶结点的值为maxNode
		//data[0]是根结点,此处root意为每次进行判断的非子叶结点

	}
}


void headSort(int *data, int count) {
    
    

	int root;
	int tmp;

	for(root = count / 2 - 1; root > 0; root--){
    
    
		adjustHeap(data, count, root);
		//从最后一个非子叶结点向上遍历
		//直到只剩最上面的一个元素,
		//每次adjust该结点的值及其左右孩子的值,
		//令大的值上浮,小的值下沉
	};

	while(count) {
    
    

		adjustHeap(data, count, 0);
		//此时调整最上面元素的值变为最大值

		tmp = data[0];
		data[0] = data[count - 1];
		//取出当前数组末尾子叶结点的小值
		data[count - 1] = tmp;
		//将本次取出的最大数放入数组最后面
		--count;
		//每次得到一个最大值后,
		//去掉一个最后面的子叶结点
		//数组长度减一
		}
}
7- 快速排序
void quickOnce(int *data, int count, int head, int tail) {
    
    
	int tmp = data[head];
	int start = head;
	int end = tail;

	if (head >= tail) {
    
    
		return;
	}

	while (head < tail) {
    
    
		while (head < tail && data[tail] >= tmp) {
    
    
			--tail;
		}
		if (head < tail) {
    
    
			data[head] = data[tail];
			++head;
		}
		while (head < tail && data[head] <= tmp) {
    
    
			++head;
		}
		if (head < tail) {
    
    
			data[tail] = data[head];
			--tail;
		}
	}
	data[head] = tmp;
	//确定第一次当作基准数的位置

	quickOnce(data, count, start, head - 1);
	quickOnce(data, count, head + 1, end);
	//对当前tmp左右的两组数再进行递归快排,
	//直到若干组数每组数只剩两个
}

void qucikSort(int *data, int count) {
    
    
	quickOnce(data, count, 0, count - 1);
}

在这里插入图片描述

8- 排序整合

通过指向函数的指针,定义一个数组存放排序类型,并用宏定义好排序名,在使用时,输入排序方式所对应的下标,可以很方便的使用
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43541094/article/details/107583499