排序算法(二):选择排序(直接选排、堆排序)

news/2024/5/19 6:47:32 标签: 堆排序, 数据结构

选择排序

  • 基本思想:

选择排序将序列分为已经有序暂时无序两部分,遍历暂时无序的部分,将其中最小的元素放到有序序列的末尾,直到全部待排序的元素排完为止。

  • 排序分类:

选择排序分为直接选择排序堆排序

一:直接选择排序

  • 排序过程:

在无序部分中选取最小的元素,若它不是这组无序部分中的第一个元素,则将它与这组无序部分中的第一个元素交换,即此元素进入有序序列,在剩余的无序部分继续重复以上步骤,直到无序部分的元素数为1时排序结束。
在这里插入图片描述

  • 代码实现:

在算法实现时,每一趟确定最小元素的时候会通过不断地比较交换来使得首位置为当前最小,交换是个比较耗时的操作。其实我们很容易发现,在还未完全确定当前最小元素之前,这些交换都是无意义的。我们可以通过设置一个变量min,每一次比较仅存储较小元素的数组下标,当轮循环结束之后,那这个变量存储的就是当前最小元素的下标,此时再执行交换操作即可。

C++实现:

#include<iostream>
#include<vector>
using namespace std;

void Selectsort(vector<int>& array){
	int sz = array.size();
	for (int disorder = 0; disorder < sz - 1; disorder++){
		// 设置min存储最小元素的下标(无序区间的首元素)
		int min = disorder;
		// 遍历寻找无序区间中的最小元素的下标
		for (int goal = disorder + 1; goal < sz; goal++){
			if (array[goal] < array[min]){
				min = goal;
			}
		}
		// 如果最小元素不是无序区间中的第一个元素则与无序区间的首元素交换
		if (min != disorder){
			swap(array[min], array[disorder]);
		}
	}
}

int main(){
	vector<int> array = { 7, 4, 5, 9, 8, 2, 1 };
	Selectsort(array);
	for (const auto& e : array){
		cout << e << " ";
	}
	cout << endl;
}

输出结果:1 2 4 5 7 8 9

C实现:

#include<stdio.h>

void Print(int* array, int sz){
	for (int cur = 0; cur < sz; cur++){
		printf("%d ", array[cur]);
	}
	printf("\n");
}

void Swap(int* num1, int* num2){
	int temp = *num1;
	*num1 = *num2;
	*num2 = temp;
}

void Selectsort(int* array, int sz){
	for (int disorder = 0; disorder < sz - 1; disorder++){
		int min = disorder;
		for (int goal = disorder + 1; goal < sz; goal++){
			if (array[goal] < array[min]){
				min = goal;
			}
		}
		if (min != disorder){
			Swap(&array[disorder], &array[min]);
		}
	}
}

int main(){
	int array[] = { 7, 4, 5, 9, 8, 2, 1 };
	int sz = sizeof(array) / sizeof(array[0]);
	Selectsort(array, sz);
	Print(array, sz);
}

输出结果:1 2 4 5 7 8 9
  • 特性总结:

1.时间复杂度:O(N2
2.空间复杂度:O(1)
3.直接选择排序是一种不稳定的排序算法

二:堆排序

  • 排序过程:

将无序序列进行建堆操作,排升序建大堆,排降序建小堆。将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端,重新调整堆结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
在这里插入图片描述

  • 代码实现:

堆排序的基础是建堆,而建堆的基础是向下调整算法(前题:左右子树都是大堆):找出左右孩子中值较大的结点和父结点进行交换,并将下标更改parent = child,child = parent×2+1继续向下调整,直到左右孩子的值都比目标结点的值小则调整完毕。(默认排升序)

建堆是从最后一个非叶子结点开始进行向下调整,将一个无序序列调整为满足堆的性质。

C++实现:

#include<iostream>
#include<vector>
using namespace std;

void Adjustdown(vector<int>& array, int start, int end){
	int parent = start;
	int child = parent * 2 + 1;
	
	while (child < end){
		// 选出左右孩子中较大的
		if (child + 1 < end && array[child + 1] > array[child]){
			child++;
		}
		// 孩子和父亲比较
		if (array[child] > array[parent]){
			// 孩子大于父亲则交换
			swap(array[child], array[parent]);
			// 向下继续传递
			parent = child;
			child = parent * 2 + 1;
		}
		else{
			break;
		}
	}
}

void Heapsort(vector<int>& array){
	// 建堆:从最后一个非叶子结点(最后一个结点的父结点)开始进行向下调整
	int sz = array.size();
	for (int index = (sz - 1 - 1) / 2; index >= 0; index--){
		Adjustdown(array, index, sz);
	}
	// 堆排序
	while (sz > 0){
		swap(array[0], array[sz - 1]);
		sz--;
		Adjustdown(array, 0, sz);
	}

}

int main(){
	vector<int> array = { 4, 6, 8, 5, 9 };
	Heapsort(array);
	for (const auto& e : array){
		cout << e << " ";
	}
	cout << endl;
}

输出结果:4 5 6 8 9

C实现:

#include<stdio.h>

void Swap(int* num1, int* num2){
	int temp = *num1;
	*num1 = *num2;
	*num2 = temp;
}

void Print(int* array, int sz){
	for (int cur = 0; cur < sz; cur++){
		printf("%d ", array[cur]);
	}
	printf("\n");
}

void Adjustdown(int* array, int start, int end){
	int parent = start;
	int child = parent * 2 + 1;

	while (child < end){
		// 选出左右孩子中较大的
		if (child + 1 < end && array[child + 1] > array[child]){
			child++;
		}
		// 孩子和父亲比较
		if (array[child] > array[parent]){
			// 孩子大于父亲则交换
			Swap(&array[child], &array[parent]);
			// 向下继续传递
			parent = child;
			child = parent * 2 + 1;
		}
		else{
			break;
		}
	}
}

void Heapsort(int* array, int sz){
	// 建堆:从最后一个非叶子结点(最后一个结点的父结点)开始进行向下调整
	for (int index = (sz - 1 - 1) / 2; index >= 0; index--){
		Adjustdown(array, index, sz);
	}
	// 堆排序
	while (sz > 0){
		Swap(&array[0], &array[sz - 1]);
		sz--;
		Adjustdown(array, 0, sz);
	}

}

int main(){
	int array[] = { 4, 6, 8, 5, 9 };
	int sz = sizeof(array) / sizeof(array[0]);
	Heapsort(array,sz);
	Print(array, sz);
}
  • 特性总结:

1.时间复杂度:O(N×logN)
2.空间复杂度:O(1)
3.堆排序是一种不稳定的排序算法


http://www.niftyadmin.cn/n/1580576.html

相关文章

C语言中常用的自定义数据类型(结构体、枚举、联合)

前言 在程序编写的过程中&#xff0c;我们难免会遇到一些复杂的元素&#xff08;例如学生&#xff1a;姓名、性别、学号&#xff09;无法用单一的内置数据类型表示&#xff0c;于是就引入了自定义数据类型来描述这些复杂的元素。 C语言中常见的自定义数据类型主要有&#xff…

C语言中宏定义和函数的区别

前言 在C语言中&#xff0c;对于一些常用或通用的代码段的封装可以有两种方式&#xff1a;函数和宏定义。 这篇博客就来带大家梳理一下对于这两种方式我们在使用时应该如何抉择&#xff0c;以及它们的区别和优缺点。 宏定义和函数的区别 从程序的执行来看&#xff1a; 函数…

ASP.NET控件开发基础4

上一篇写了有关回传的一些东西,这次我本来不知道该写什么的,因为各方面的关联太多了,最后我还是想,还是慢慢一点点的写吧.这次讲WebControl一.从继承WebControl开始在第二篇教程中,重点介绍了Render()方法的使用,用来呈现控件,但从Control类继承的控件尚未发挥asp.net控件的作用…

排序算法(三):交换排序(冒泡排序、快速排序的递归与非递归)

交换排序 基本思想&#xff1a; 所谓交换&#xff0c;就是根据序列中两个元素键值的比较结果来交换这两个元素在序列中的位置&#xff0c;将键值较大的元素向序列的尾部移动&#xff0c;键值较小的元素向序列的前部移动。 排序分类&#xff1a; 交换排序分为冒泡排序和快速…

找不到org.apache.commons.dbcp.BasicDataSource的解决方法

导入commons-dbcp.jar这个包就可以了。转载于:https://www.cnblogs.com/xwdreamer/archive/2010/11/30/2297068.html

STL序列式容器:vector(基本操作+模拟实现)

前言 vector类是STL中的另一大容器&#xff0c;经过封装&#xff0c;vector是一个可变长度并且拥有各种功能的顺序表&#xff0c;在其内部可以利用数组实现。vector和string在物理与逻辑结构上十分相似&#xff0c;不过vector是一个模板类&#xff0c;我们可以在其中存放任意类…

Silverlight图片元素——创建DeepZoom应用程序

Silverlight图片元素——创建DeepZoom应用程序在上一篇博文中&#xff0c;我们介绍了Deep Zoom Composer的使用方法&#xff0c;下面我们就来自己做一个简单的图片缩放项目吧!首先我们来看一下我们所要实现的效果&#xff1a;现在我们就用到了我们用Deep Zoom Composer工具生成…

排序算法(四):归并排序(递归写法与非递归写法)

归并排序 基本思想&#xff1a; 归并排序是一种采用分治策略&#xff0c;将待排序序列分成若干个不可再分的子序列&#xff0c;先使每个子序列有序&#xff0c;再使子序列段间有序的高效排序算法。 排序过程&#xff08;分治算法&#xff09;&#xff1a; 分的过程 这种结…