数据结构:冒泡排序,选择排序,插入排序,希尔排序的实现分析

✨✨小新课堂开课了,欢迎欢迎~✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:数据结构与算法

小新的主页:编程版小新-CSDN博客

 1.冒泡排序

1.1算法思想

冒泡排序的基本思想就是,遍历原数组的元素,比较相邻的两个元素,如果这两个元素的顺序不对,就是把这两个元素交换,直到都不需要交换的时候,排序结束。

1.2具体步骤

比较相邻的两个元素,把大的(小的)往后换,就这样重复此步骤,每遍历完数组一次,就能将最大的(最小的)元素放在最后。重复遍历该数组,直至都不需要交换的时候,排序就结束了。

1.3动图演示

24年-05月27日--排序/动图/冒泡排序.gif

1.4代码实现

void Swap(int*a,int*b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
//冒泡排序
void BubbleSort(int* arr, int len)
{
	for (int i = 0; i < len - 1; i++)//比较次数
	{
		for (int j = 0; j < len-1-i; j++)//单趟比较
		{
			if (arr[j] > arr[j + 1])
			{
				Swap(&arr[j], &arr[j + 1]);
			}
		}
	}
}

有的时候不用遍历数组len次,排序就已经排好了,我们知道遍历完数组一次后,如果不存在交换,那就不用再继续比较了。此时排序已经排好了。

优化:

void Swap(int*a,int*b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
//冒泡排序
void BubbleSort(int* arr, int len)
{
    int flag = 0;
	for (int i = 0; i < len - 1; i++)//比较次数
	{
		for (int j = 0; j < len-1-i; j++)//单趟比较
		{
			if (arr[j] > arr[j + 1])
			{
				Swap(&arr[j], &arr[j + 1]);
                flag = 1;
			}
		}

        if(flag ==0)//未发生交换,排序已经完成
           break;
	}
}

1.5复杂度分析

时间复杂度:这里很容易就能看出来是O(N^2)。以最坏的情况来看,n-1+n-2+n-3+……+2+1=n*(n-1)/2。

空间复杂度: 因为没有额外的开空间,所以空间复杂度为O(1)。

1.6稳定性分析

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

简而言之,相同值相对位置不变称之为稳定。

 冒泡排序是相邻位置的元素比较,决定交不交换,一样的元素,在排序过程中相对位置不会发生变化,因此冒泡排序是稳定的

2.选择排序

2.1算法思想

遍历原数组,找出最小或者最大的那个元素,与起始位置或则末尾的元素交换,每遍历完一次,起始位置向后移(末尾的位置往前移),当起始位置移到最后一个位置时(末尾位置移到数组的第一个元素的位置时),排序结束。

2.2具体步骤

遍历数组,找到最大或者最小的元素,与起始位置交换。

从剩余的元素中找继续找最大或最小的元素,排到已排序序列的末尾。

以此类推,直到所有的元素都排列完毕。

2.3动图演示

2.4代码实现

基于动态演示实现的代码:

void SelectSort(int* arr, int len)//选择排序
{
	for (int i = 0; i < len - 1; i++)//起始位置
	{
		int mini = i;
		for (int j = i + 1; j < len; j++)//寻找最小元素
		{
			if (arr[j] < arr[mini])
			{
				mini = j;
			}
		}
		swap(&arr[mini], &arr[i]);
	}
}

上面的代码效率比较低,我们在遍历一遍数组的时候,可以找到该数组中最大和最小的元素,让最大的元素与end交换,最小的元素与begin交换,不断的这样缩小区间,完成排序。

这里要注意的是,我们在交换的时候,可能会改变最大或最小元素的位置 。

//选择排序

void Swap(int*a,int*b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}


void SelectSort(int* arr, int len)
{
	int begin = 0;
	int end = len - 1;
	while (begin < end)
	{
		int mini = begin;
		int maxi = begin;
		for (int i = begin + 1; i <=end; i++)
		{
			//遍历一遍找最大和最小
			if (arr[i] > arr[maxi])
			{
				maxi = i;
			}

			if (arr[i] < arr[mini])
			{
				mini = i;
			}

		}
		Swap(&arr[begin], &arr[mini]);

		if (maxi == begin)//如果begin和maxi重合,就要对maxi更正
		{
			maxi = mini;
		}

		Swap(&arr[end], &arr[maxi]);
		begin++;
		end--;
	}
}

2.5复杂度分析

时间复杂度:优没优化,算时间复杂度的时候都是等差数列,时间复杂度为O(N^2)。

空间复杂度:没有额外开辟空间,因此空间复杂度为O(N)。

2.6稳定性分析

3.插入排序

3.1算法思想

把待排元素逐一与已排序的部分元素进行比较,把它插入到已排序序列中的适当位置,直到把元素全部插入完毕。

3.2具体步骤

把第一个元素看成有序序列,从第二个元素开始,将该元素与前面已排好序的元素比较,如果该元素比较小,就将前面的元素依次往后移动一位,将该元素插入到腾出的位置上。

重复上面的插入过程,直至整个序列都被排序完成。

3.3动图演示

3.4代码实现

// 插入排序
void InsertSort(int* a, int n)
{
	//【0-end】有序,将end+1的值插入到区间内,形成新的区间
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			
			if (a[end] > tmp)
			{
				a[end + 1] = a[end];//往后移
				end--;
			}
			else
			{
				break;//找到合适的位置了
                      //也有可能合适的位置就是数组的起始位置,没有进break,此时end为-1越界,在
                      // end+1的位置插入,就是把该元素插入到起始位置
			}
		}
		a[end + 1] = tmp;//在腾出来的位置插入该元素
	}

}

3.5复杂度分析

时间复杂度:以最坏的情况,逆序来算,移动的次数是等差数列的前n-1项和,O(N^2)。

空间复杂度:没有额外开辟空间,空间复杂度为O(1)。

3.6稳定性分析

插入排序不会改变相同元素的相对位置,在前面的和在后面的那个在插入到腾出的位置后,还是一前一后。因此插入排序稳定

4.希尔排序

4.1算法思想

希尔排序是对插入排序的改进。它先将整个待排的序列分割成若干个子序列,对每一个子序列分别进行插入排序,然后不断缩小缩小子序列的间隔,重复这个过程直至间隔为1 时进行一次完整的插入排序。

4.2具体步骤

希尔排序分为两步:1.预排序(使数组接近有序) 2.插入排序

选取一个gap,间隔gap个数据为一组,把整个数组进行分组,一共gap组。

以gap为基准,对其进行插入排序。

不断缩小gap,直至gap为1,进行一次完整的插入排序。

gap>1时是在进行预排序,gap==1是在进行直接插入排序。          

4.3动图演示

4.4代码实现

// 希尔排序
//希尔排序分为两步:第一步是预排序(使其接近有序),第二步是进行插入排序
void ShellSort(int* a, int n)
{
	int gap = n;//gap代表每间隔gap个为一组即一共分成gap组
	while (gap > 1)
	{
		//gap>1是进行预排序
		//gap==1是在进行插入排序
		gap = gap / 3 + 1;//+1是保证最后一组gap一定为1,
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];//i之所以小于n-gap;是防止这里越界
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	
	}
}

4.5复杂度分析

时间复杂度:希尔排序的时间复杂度可以直接记,因为比较难算。O(N^1.3)

空间复杂度:没有额外开辟空间,空间复杂度为O(1)。

4.6稳定性分析

在排序过程中,可能相同数据分到不同的组,这个无法控制,因此希尔排序不稳定

  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
冒泡排序希尔排序和快速排序都是经典的排序算法,下面分别介绍一下它们的思路和实现。 1. 冒泡排序 冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就交换位置。遍历数列的工作会重复地进行,直到没有再需要交换的元素,排序就完成了。 代码实现: ``` void bubbleSort(int arr[], int n) { for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - i - 1; j++) { if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } ``` 2. 希尔排序 希尔排序插入排序的一种更高效的改进版本,它的基本思想是将原来要排序的数据分成若干个小组,对每个小组分别进行插入排序,随着排序的进行,逐渐减小小组的大小,直到小组的大小为1,完成排序。 代码实现: ``` void shellSort(int arr[], int n) { for (int gap = n / 2; gap > 0; gap /= 2) { for (int i = gap; i < n; i++) { int temp = arr[i]; int j; for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) { arr[j] = arr[j - gap]; } arr[j] = temp; } } } ``` 3. 快速排序 快速排序是一种分治的排序算法,它的基本思想是通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,然后再分别对这两部分记录继续进行排序,以达到整个序列有序的目的。 代码实现: ``` void quickSort(int arr[], int left, int right) { if (left >= right) return; int i = left, j = right, pivot = arr[(left + right) / 2]; while (i <= j) { while (arr[i] < pivot) i++; while (arr[j] > pivot) j--; if (i <= j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; i++; j--; } } quickSort(arr, left, j); quickSort(arr, i, right); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值