数据结构:排序有方法(七大内部排序图文详解)

内部排序

定义:全部记录都可以同时调入内存进行的排序

排序

定义:	设有记录序列:{ R1、R2   ………..   Rn  }
 		其相应的关键字序列为: { K1、K2   ………..   Kn  }; 
 		若存在一种确定的关系: Kx <= Ky <= … <= Kz则将记录序列 { R1、R2   ………..   Rn  } 
 		排成按该关键字有序的序列 { Rx、Ry   ………..   Rz  } 的操作,称之为排序。

注意:关系是任意的,如通常使用的小于、大于等关系。

内部排序算法

直接插入排序
例:36、24、10、6、12存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将其排序。结果仍保存在下标为 1 至 5 的元素之中。
数据结构:排序有方法(七大内部排序图文详解)_第1张图片

r[0] 用作哨兵。共执行 5 遍操作。
每遍操作:

  1. 先将元素复制内容放入num[0],
  2. 再将本元素同已排序的序列,从尾开始进行比较。在已排序的序列中寻找自己的位置,进行插入
  3. 或者寻找不到,则一直进行到哨兵为止。意味着本元素最小,应该放在 num[1] 。

每一遍,排序的序列将增加一个元素。如果序列中有 n 个元素,那么最多进行n 遍即可。

数据结构:排序有方法(七大内部排序图文详解)_第2张图片

void insert_sort(int num[],int len)
{
     
	for (int i = 2; i < len; i++) {
     		//遍历数组排序
		num[0] = num[i];
		int j = i;
		while (j--) {
     					//在前i个有序数组找插入位置
			if (num[0] < num[j]) {
     		//第i个元素小于第j个元素,将第j个元素向后移
				num[j + 1] = num[j];
			}
			else if (j == 0) {
     			//若j==0,则第i个元素在前i个元素中最小
				num[1] = num[0];
			}
			else {
     
				num[j + 1] = num[0];//第i个元素大于第j个元素,则第j+1个元素等于第i个元素
				break;
			}
		}
	}
	for (int i = 1; i < len; i++) {
     
		printf("%d ", num[i]);
	}
}

时间复杂度为:T(n)=O(n)
辅助空间:S(n)=O(1)

折半插入排序

用折半查找方法确定插入位置的排序
数据结构:排序有方法(七大内部排序图文详解)_第3张图片

void bininsert_sort(int num[], int len)
{
     
	for (int i = 2; i < len; i++) {
     
		num[0] = num[i];					//更新标志
		int low = 1, hight = i - 1,mid;		//初始化头尾指针		
		while (low <= hight) {
     				//折半寻找合适位置
			mid = (hight + low) / 2;
			if (num[0] < num[mid]) {
     
				hight = mid - 1;
			}
			else {
     	
				low = mid + 1;
			}
		}
		for (int j = i - 1; j >= low; j--) {
     //循环后移元素,为插入准备
			num[j + 1] = num[j];
		}
		num[low] = num[0];					//插入
	}
	for (int i = 1; i < len; i++) {
     
		printf("%d ", num[i]);
	}
}

时间复杂度:T(n)=O(n²)
辅助空间:S(n)=O(1)

希尔排序

  1. 先取一个正整数d1

  2. 然后取d2

  3. 直至di=1,即所有记录放进一个组中排序为止。

    希尔排序实际是步长递减的多次直接插入排序过程。
    

特点:
子序列的构成不是简单的“逐段分割”,而是将相隔某个增量的记录组成一个子序列。希尔排序可提高排序速度,因为分组后n值减小,n²更小,而T(n)=O(n²),所以T(n)从总体上看是减小了,关键字较小的记录跳跃式前移,在进行最后一趟增量为1的插入排序时,序列已基本有序。辅助空间:S(n)=O(1)

数据结构:排序有方法(七大内部排序图文详解)_第4张图片

void shell_sort(int num[], int len)
{
     
	int i, j;
	for (i = len/2; i >= 1; i /=2) {
     			//循环n趟
		for (j = 1 + i; j < len; j++) {
     			//循环i次
			num[0] = num[j];
			int k = j - i;
			while (k > 0 && num[0] < num[k]) {
     	//每个小组内比较移动
				num[k + i] = num[k];
				k -= i;
			}
			num[k + i] = num[0];				//插入位置要比k大i
		}
	}
	for (int i = 1; i < len; i++) {
     
		printf("%d ", num[i]);
	}
}

增量序列取法:
最后一个增量值必须为1;

冒泡排序

将第一个记录的关键字与第二个记录的关键字进行比较,若为逆序r[1].key>r[2].key,则交换;然后比较第二个记录与第三个记录;依次类推,直至第n-1个记录和第n个记录比较为止——第一趟冒泡排序,结果关键字最大的记录被安置在最后一个记录上

对前n-1个记录进行第二趟冒泡排序,结果使关键字次大的记录被安置在第n-1个记录位置
重复上述过程,直到“在一趟排序过程中没有进行过交换记录的操作”为止

void bubble_sort(int num[], int len)
{
     
	bool flag = 1;							//交换标志
	int i = len;
	while (i>2&&flag) {
     
		flag = 0;
		for (int j = 1; j + 1< i; j++) {
     
			if (num[j] > num[j + 1]) {
     		
				int tem = num[j];
				num[j] = num[j + 1];
				num[j + 1] = tem;
				flag = 1;
			}
		}
		i--;
	}
	for (int i = 1; i < len; i++) {
     
		printf("%d ", num[i]);
	}
}

时间复杂度T(n)=O(n²)
辅助空间:S(n)=O(1)

快速排序
通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录进行排序,以达到整个序列有序

排序过程:

  1. 对num[s……t]中记录进行一趟快速排序,附设两个指针i和j,初始时令i=s,j=t,x=num[0]
  2. 首先从j所指位置向前搜索第一个关键字小于x的记录,并和x交换
  3. 再从i所指位置起向后搜索,找到第一个关键字大于x的记录,和x交换
  4. 重复上述两步,直至i==j为止
  5. 再分别对两个子序列进行快速排序,直到每个子序列只含有一个记录为止

数据结构:排序有方法(七大内部排序图文详解)_第5张图片

void quick_sort(int num[], int low, int hight)
{
     
	int  i = low, j = hight, x = num[i];
	if (low >= hight)
		return;
	while (i < j) {
     						//i>j说明排序完成
		while (i < j&&num[j] >= x)		//i指向标志元素,移动j找位置
			j--;						
		if (i < j) {
     
			num[i] = num[j];			//使i指向的元素等于j指向的元素指向的元素
			i++;
		}
		//接下来移动i指针
		while (i < j&&num[i] <= x)
			i++;
		if (i < j) {
     
			num[j] = num[i];
			j--;
		}
	}
	num[i] = x;					//找到放置标志元素位置,最后
	k_sort(num, low, j - 1);	//递归排序x位置左边的元素
	k_sort(num, j + 1, hight);//递归排序x位置右边的元素
}

最好情况(每次总是选到中间值作枢轴) T(n)=O(nlog2n)
最坏情况(每次总是选到最小或最大元素作枢轴)T(n)=O(n²)
辅助空间:需栈空间以实现递归
最坏情况:S(n)=O(n)
一般情况:S(n)=O(log2n)

选择排序

排序过程:

  1. 首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与第一个记录交换;
  2. 再通过n-2次比较,从剩余的n-1个记录中找出关键字次小的记录,将它与第二个记录交换;
  3. 重复上述操作,共进行n-1趟排序后,排序结束数据结构:排序有方法(七大内部排序图文详解)_第6张图片
void smp_sort(int num[], int len)
{
     
	for (int i = 1; i < len ; i++) {
     
		int min = i;
		for (int j = i + 1; j < len; j++) {
     
			if (num[j] < num[min]) {
     
				min = j;
			}
		}
		if (i != min) {
     
			int tem = num[i];
			num[i] = num[min];
			num[min] = tem;
		}
	}
	for (int i = 1; i < len; i++) {
     
		printf("%d ", num[i]);
	}
}

时间复杂度:T(n)=O(n²)
辅助空间:S(n)=O(1)

堆排序

排序过程:
将无序序列建成一个堆,得到关键字最小(或最大)的记录;输出堆顶的最小(大)值后,使剩余的n-1个元素重又建成一个堆,则可得到n个元素的次小值;重复执行,得到一个有序序列

大顶堆:满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,简单来说就是双亲节点的值总是大于孩子结点,而兄弟节点之间没有约束,左孩子可以大于右孩子,也可以小于右孩子。
小顶堆:满足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆,双亲节点的值总是小于孩子结点。
特点: 可将堆序列看成完全二叉树,则堆顶元素(完全二叉树的根)必为序列中n个元素的最小值或最大值。

堆排序要解决的问题:

  • 如何在输出堆顶元素之后,调整剩余元素,使之成为一个新的堆?

第二个问题解决方法——筛选

输出堆顶元素之后,以堆中最后一个元素替代之;然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换;重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”。

例:
小顶堆排序
数据结构:排序有方法(七大内部排序图文详解)_第7张图片
数据结构:排序有方法(七大内部排序图文详解)_第8张图片

数据结构:排序有方法(七大内部排序图文详解)_第9张图片

void buildd_1(int num[], int len)						//从根节点向下建小顶堆
{
     
	for (int i = 1; i < len; i++) {
     
		int j = i;
		while (j != 0 && num[j] < num[(j - 1) / 2]) {
     //孩子节点逐一与双亲节点比较
			int tem = num[j];
			num[j] = num[(j - 1) / 2];
			num[(j - 1) / 2] = tem;
			j = (j - 1) / 2;
		}
	}
}
void buildd_2(int num[],int i, int len) //自底向上建小顶堆
{
     
	int child = 2 * i + 1;
	int parent = i;
	while (child < len) {
     //小的孩子节点与双亲节点比较
		if (child + 1 < len && num[child] > num[child + 1]) {
     //找值小的孩子结点下标
			child++;
		}
		if (num[parent] > num[child]) {
     //改变了前边部分有序的值,就需要从新与变动过的地方比较
			int tem = num[parent];
			num[parent] = num[child];
			num[child] = tem;
			parent = child;//值变动,改变双亲的下标值,再比较
		}
		else//没有变动则说明当前序列符合小顶堆规则,退出循环比较
			break;
		child = child * 2 + 1;//更新孩子结点下标
	}
}
void buildd_3(int num[], int len)			//自上而下建大顶堆
{
     
	for (int i = 1; i < len; i++) {
     
		int j = i;
		while (j != 0 && num[j] > num[(j - 1) / 2]) {
     
			int tem = num[j];
			num[j] = num[(j - 1) / 2];
			num[(j - 1) / 2] = tem;
			j = (j - 1) / 2;
		}
	}
}*/

void buildd_4(int num[], int i, int len)				//自下而上建大顶堆
{
     
	int child = 2 * i + 1;
	int parent = i;
	while (child < len) {
     
		if (child + 1 < len && num[child] < num[child + 1]) {
     
			child++;
		}
		if (num[parent] < num[child]) {
     
			int tem = num[parent];
			num[parent] = num[child];
			num[child] = tem;
			parent = child;
		}
		else
			break;
		child = child * 2 + 1;
	}
}
void buildHeap(int array[], int size) {
     
	for (int i = size / 2 - 1; i >= 0; i--) {
      //倒数第二排开始,创建大顶堆,必须从下往上比较
		buildd__2(array, i, size); 
		//buildd__4(array, i, size);                // 否则有的不符合大顶堆定义
	}
}

int main()
{
     
	int num[10] = {
      0,12,4,54,3,5,78,34,22,90 };
	
	buildHeap(num, 10);
	//自上而下堆排序
	for (int i = 9; i >= 0; i--) {
     //建好堆后交换根结点和当前序列最后一个节点
		int tem = num[i];
		num[i] = num[0];
		num[0] = tem;
		buildd_2(num, 0, i);
		//buildd_4(num, 0, i);
	}
	/*	自上而下堆排序
		buildd_1(num, 0, i);
		for (int i = 9; i >= 0; i--) {
		int tem = num[i];		
		num[i] = num[0];
		num[0] = tem;
		buildd_1(num, 0, i);
		//buildd_3(num, 0, i);
	}*/
	for (int i = 0; i < 10; i++) {
     
		printf("%d ", num[i]);
	}
	return 0;
}

时间复杂度:最坏情况下T(n)=O(nlogn);
辅助空间:S(n)=O(1)。

内部排序基本就这些内容了,朋友们下次见!!!

你可能感兴趣的:(数据结构,排序算法,快速排序,数据结构,算法)