排序(前篇)

2024-05-29 19:36
文章标签 排序 前篇

本文主要是介绍排序(前篇),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.排序的概念及其运用

2.插入排序的概念及实现

3.希尔排序的概念及实现 

4.选择排序概念及实现

总代码(对比各个排序在大量的数据情况排序所化的时间):

1.排序的概念及其运用

1.1排序的概念


排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次
序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不断地在内外存之间移动数据的排序。

1.2排序的运用

在网上购物时,会出现综合,销量及评论数等,会以此为参考来排序,选出综合最高,销量最高的产品,以及院校排名这些,都使用了排序。

(以下图片仅参考)

1.3 常见的排序算法

2.插入排序的概念及实现

2.1插入排序的概念

就像扑克牌一样,每拿到一张牌都要放进已经排好序的牌里面,用这张牌跟里面的比较,再把这张牌放到合适的位置,插入排序的原理就是把数插入已经排好序的数里面,比较排好数的里面的每一个数,找到位置就插入。

代码:

#include<stdio.h>
void InsertSort(int* a,int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = a[end+1];while (end >= 0){if (tmp < a[end]){a[end+1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}
int main()
{int a[10] = { 3,4,5,1,2,6,7,8,2 };int size = sizeof(a) / sizeof(a[0]);InsertSort(a,size);for (int i = 0; i < 9; i++){printf("%d ", a[i]);}return 0;
}

代码分析: 

while循环出去有俩种情况,1是触发break,二是while循环的条件不满足,在循环外把tmp赋给数组下标为end+1的位置, 是因为如果是while循环结束的话刚好也可以赋值,但是在里面的话,因为while结束的就无法把数据加回去(移动带来覆盖,会有一位重复,需要赋值来填回去)。

(动态图关于插入排序24年-05月27日--排序/动图/插入排序.gif · 比特杭哥/113期 - Gitee.com)

2.2冒牌排序的时间复杂度

冒泡排序的最坏情况:

假设if条件每次都会执行,那么时间复杂度就为O(N^2).

最好的情况:

上面冒泡代码不能做到O(N),需要对其优化才能使冒泡排序达到最好情况,

设置一个flag去判断是否触发了if条件,如果触发了就置为0,后面再去检验值是否为一开始的初始值,是就说明没有触发if条件,没触发就说明顺序是排好的,只执行了里面的if判断,所以时间复杂度是O(N)。

void BubbleSort(int* a, int n)
{for (int i = 0; i < n; i++){int flag = 1;for (int j = 0; j < n-i-1; j++){if (a[j] > a[j + 1]){int tmp = a[j];a[j] = a[j + 1];a[j + 1] = tmp;flag = 0;}}if (flag == 1){break;}}
}

2.3插入排序的时间复杂度

按照最坏打算:

既逆序情况

最好的情况:

当数据是以升序排序时,里面的循环不执行(会从break跳出),则只有for循环执行,所以时间复杂度就为O(N)。

插入排序与冒泡排序的时间复杂度虽然是一样的,但插入排序是比冒泡排序好,因为插入排序最坏的情况很难达到,只有是逆序(或者接近这个情况)的情况下,插入排序才会慢下来,而冒泡排序一般都是O(N^2),因为冒泡每次遍历选出其中最大的放最后,if的条件任意触发。

void BubbleSort(int* a, int n)//冒泡排序
{for (int i = 0; i < n; i++){for (int j = 0; j < n-i-1; j++){if (a[j] > a[j + 1]){int tmp = a[j];a[j] = a[j + 1];a[j + 1] = tmp;}}}
}

3.希尔排序的概念及实现 

3.1希尔排序概念

希尔排序与插入排序密不可分,基于插入排序来实现希尔排序的。

希尔排序:首先会对数据进行预排序(让数组接近有序),再进行插入排序(因为插入排序怕的是数组是逆序情况)。

预排序:把数组在逻辑上分成gap组,gap是间隔的距离,然后对每个gap组进行插入排序,这样可以使大的数据跑到比较后面的位置,小的数据会跑到比较前面的位置,gap设置的越大,大的数据越快到后面,小的数据越快到前面,越不接近有序,gap设置的越小,数据跳的慢,但是会越接近有序的情况,gap为1的时候就是插入排序了,希尔排序就是慢慢把gap的值调小(设置gap是为了最后一次gap为一做铺垫,为了插入排序能更快的完成),最后gap为一时执行插入排序。

3.2希尔排序的代码实现

代码实现1:

void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}
void ShellSort1(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;for (int i = 0; i < n - gap; i++){int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){swap(&a[end], &a[end + gap]);end--;}elsebreak;}a[end + gap] = tmp;}}
}

代码实现二:

void ShellSort2(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;for (int j = 0; j < gap; j++){for (int i = j; i < n - gap; i += gap){int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){swap(&a[end], &a[end + gap]);end -= gap;}elsebreak;}a[end + gap] = tmp;}}}
}

代码分析:上面俩个代码的样子不一样,但是效率是一样的俩种都可以使用,

n是数组的个数,一般gap的值都为个数的三分之一+1,加一是为了最后gap的值能达到一进行插入排序,gap/2的效率不如gap/3(大量数据实验得出的),代码一是多个gap组同时进行预排序,每次i加一,但是比的是gap距离的数据,gap第一组的第一个跟其gap距离的比完后,第二个gap组的第一个跟其gap距离的比,再到第三个gap组的第一个与其gap距离的数据比较,再到第二个等等,代码二则是第一个gap组会一直比完才到第二个gap组比。还需要注意的是循环终止条件是n-gap,是因为循环里面有end+gap,n+gap-gap刚好是为n,也就是数组最后一个数据下标的下一个,有效数据的下一个是不能访问的,会造成越界访问,数组最多能访问到n-1的位置,如果是i<n,则a[end+gap]就会越界了。

3.3希尔排序的时间复杂度(简单分析)

希尔排序的时间复杂度为O(N^1.3)。

4.选择排序概念及实现

选择排序就是遍历数组找到最小的数据并把它放在最前面,可以对它进行优化,就是在遍历的同时把最大和最小的数据找出来并放在俩边

代码实现:

void SelectSort(int* a, int n)
{int begin, end;begin = 0;end = n - 1;while (begin < end){int mini, max;mini = max = begin;for (int i = begin+1; i <= end; i++){if (a[mini] > a[i]){mini = i;}if (a[max] < a[i]){max = i;}}swap(&a[mini], &a[begin]);swap(&a[max], &a[end]);end--;begin++;}	
}

代码分析:

因为是找最大和最小并放在俩边,所以begin和end会慢慢往中间靠近,在定义最小值和最大值去和数组的每一个数比较,比定义的最大值大就交换一下下标,比最小值小就交换 下标,遍历完后在交换值,并且把begin++和end--,因为begin和end都放好了对应的值,要放其它位置的值。

总代码(对比各个排序在大量的数据情况排序所化的时间):

test.c文件:

#include"Sort.h"void TestInsertSort()
{int a[] = { 2,4,1,7,8,3,9,2 };InsertSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestShellSort()
{int a[] = { 9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5 };//InsertSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));ShellSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestSelectSort()
{//int a[] = { 9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5 };//InsertSort(a, sizeof(a) / sizeof(int));//int a[] = { 2,4,1,7,8,3,9,2 };int a[] = { 9,1,2,5,7,4,6,3 };PrintArray(a, sizeof(a) / sizeof(int));SelectSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestOP()
{srand(time(0));const int N = 1000000;int* a1 = (int*)malloc(sizeof(int) * N);int* a2 = (int*)malloc(sizeof(int) * N);int* a3 = (int*)malloc(sizeof(int) * N);int* a4 = (int*)malloc(sizeof(int) * N);int* a5 = (int*)malloc(sizeof(int) * N);int* a6 = (int*)malloc(sizeof(int) * N);int* a7 = (int*)malloc(sizeof(int) * N);for (int i = 0; i < N; ++i){a1[i] = rand()+i;a2[i] = a1[i];a3[i] = a1[i];a4[i] = a1[i];a5[i] = a1[i];a6[i] = a1[i];a7[i] = a1[i];}int begin1 = clock();//InsertSort(a1, N);int end1 = clock();int begin2 = clock();ShellSort(a2, N);int end2 = clock();int begin3 = clock();//SelectSort(a3, N);int end3 = clock();int begin4 = clock();HeapSort(a4, N);int end4 = clock();int begin5 = clock();//QuickSort(a5, 0, N - 1);int end5 = clock();int begin6 = clock();//MergeSort(a6, N);int end6 = clock();int begin7 = clock();//BubbleSort(a7, N);int end7 = clock();printf("InsertSort:%d\n", end1 - begin1);printf("ShellSort:%d\n", end2 - begin2);printf("SelectSort:%d\n", end3 - begin3);printf("HeapSort:%d\n", end4 - begin4);printf("QuickSort:%d\n", end5 - begin5);printf("MergeSort:%d\n", end6 - begin6);printf("BubbleSort:%d\n", end7 - begin7);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);free(a7);
}int main()
{//TestInsertSort();//TestShellSort();TestSelectSort();//TestOP();return 0;
}

Sort.h文件:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>void PrintArray(int* a, int n);// 有实践意义
void InsertSort(int* a, int n);void ShellSort(int* a, int n);void ShellSort(int* a, int n);void HeapSort(int* a, int n);// 适合教学,实践中没啥价值
void BubbleSort(int* a, int n);void SelectSort(int* a, int n)

Sort.c文件:

#include"Sort.h"void PrintArray(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}// 时间复杂度:O(N^2)  什么情况最坏:逆序
// 最好:顺序有序,O(N)
// 插入排序
void InsertSort(int* a, int n)
{//  [0, n-1]for (int i = 0; i < n - 1; i++){// [0, n-2]是最后一组// [0,end]有序 end+1位置的值插入[0,end],保持有序int end = i;int tmp = a[end + 1];while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];--end;}else{break;}}a[end + 1] = tmp;}
}// O(N^1.3)
//void ShellSort(int* a, int n)
//{
//	/*int gap = 3;
//	for (int j = 0; j < gap; j++)
//	{
//		for (size_t i = j; i < n - gap; i += gap)
//		{
//			int end = i;
//			int tmp = a[end + gap];
//			while (end >= 0)
//			{
//				if (tmp < a[end])
//				{
//					a[end + gap] = a[end];
//					end -= gap;
//				}
//				else
//				{
//					break;
//				}
//			}
//			a[end + gap] = tmp;
//		}
//	}*/
//
//	int gap = n;
//	while (gap > 1)
//	{
//		// +1保证最后一个gap一定是1
//		// gap > 1时是预排序
//		// gap == 1时是插入排序
//		gap = gap / 3 + 1;
//
//		for (size_t i = 0; i < n - gap; ++i)
//		{
//			int end = i;
//			int tmp = a[end + gap];
//			while (end >= 0)
//			{
//				if (tmp < a[end])
//				{
//					a[end + gap] = a[end];
//					end -= gap;
//				}
//				else
//				{
//					break;
//				}
//			}
//			a[end + gap] = tmp;
//		}
//		//printf("gap:%2d->", gap);
//		//PrintArray(a, n);
//	}
//}// O(N ^ 1.3)
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){// +1保证最后一个gap一定是1// gap > 1时是预排序// gap == 1时是插入排序gap = gap / 3 + 1;for (size_t i = 0; i < n - gap; ++i){int end = i;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}void AdjustDown(int* a, int n, int parent)
{// 先假设左孩子小int child = parent * 2 + 1;while (child < n)  // child >= n说明孩子不存在,调整到叶子了{// 找出小的那个孩子if (child + 1 < n && a[child + 1] > a[child]){++child;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapSort(int* a, int n)
{// 向下调整建堆 O(N)for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, n, i);}// O(N*logN)int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);--end;}
}// O(N^2) 最坏
// O(N)   最好
void BubbleSort(int* a, int n)
{for (int j = 0; j < n; j++){// 单趟int flag = 0;for (int i = 1; i < n - j; i++){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);flag = 1;}}if (flag == 0){break;}}
}void SelectSort(int* a, int n)
{int begin = 0, end = n - 1;while (begin < end){int mini = begin, maxi = begin;for (int i = begin + 1; i <= end; ++i){if (a[i] > a[maxi]){maxi = i;}if (a[i] < a[mini]){mini = i;}}Swap(&a[begin], &a[mini]);Swap(&a[end], &a[maxi]);++begin;--end;}
}

这篇关于排序(前篇)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1014450

相关文章

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

hdu 1285(拓扑排序)

题意: 给各个队间的胜负关系,让排名次,名词相同按从小到大排。 解析: 拓扑排序是应用于有向无回路图(Direct Acyclic Graph,简称DAG)上的一种排序方式,对一个有向无回路图进行拓扑排序后,所有的顶点形成一个序列,对所有边(u,v),满足u 在v 的前面。该序列说明了顶点表示的事件或状态发生的整体顺序。比较经典的是在工程活动上,某些工程完成后,另一些工程才能继续,此时

《数据结构(C语言版)第二版》第八章-排序(8.3-交换排序、8.4-选择排序)

8.3 交换排序 8.3.1 冒泡排序 【算法特点】 (1) 稳定排序。 (2) 可用于链式存储结构。 (3) 移动记录次数较多,算法平均时间性能比直接插入排序差。当初始记录无序,n较大时, 此算法不宜采用。 #include <stdio.h>#include <stdlib.h>#define MAXSIZE 26typedef int KeyType;typedef char In

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in

学习记录:js算法(二十八):删除排序链表中的重复元素、删除排序链表中的重复元素II

文章目录 删除排序链表中的重复元素我的思路解法一:循环解法二:递归 网上思路 删除排序链表中的重复元素 II我的思路网上思路 总结 删除排序链表中的重复元素 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 图一 图二 示例 1:(图一)输入:head = [1,1,2]输出:[1,2]示例 2:(图

鸡尾酒排序算法

目录 引言 一、概念 二、算法思想 三、图例解释 1.采用冒泡排序:   2.采用鸡尾酒排序:  3.对比总结 四、算法实现  1.代码实现  2.运行结果 3.代码解释   五、总结 引言 鸡尾酒排序(Cocktail Sort),也被称为双向冒泡排序,是一种改进的冒泡排序算法。它在冒泡排序的基础上进行了优化,通过双向遍历来减少排序时间。今天我们将学习如何在C

快速排序(java代码实现)

简介: 1.采用“分治”的思想,对于一组数据,选择一个基准元素,这里选择中间元素mid 2.通过第一轮扫描,比mid小的元素都在mid左边,比mid大的元素都在mid右边 3.然后使用递归排序这两部分,直到序列中所有数据均有序为止。 public class csdnTest {public static void main(String[] args){int[] arr = {3,

O(n)时间内对[0..n^-1]之间的n个数排序

题目 如何在O(n)时间内,对0到n^2-1之间的n个整数进行排序 思路 把整数转换为n进制再排序,每个数有两位,每位的取值范围是[0..n-1],再进行基数排序 代码 #include <iostream>#include <cmath>using namespace std;int n, radix, length_A, digit = 2;void Print(int *A,

常用排序算法分析

1. 插入排序 1.1 性能分析 时间复杂度O(n^2), 空间复杂度O(1) 排序时间与输入有关:输入的元素个数;元素已排序的程度。 最佳情况,输入数组是已经排好序的数组,运行时间是n的线性函数; 最坏情况,输入数组是逆序,运行时间是n的二次函数。 1.2 核心代码 public void sort(){int temp;for(int i = 1; i<arraytoSort.