冒泡排序 & 选择排序 &

一、冒泡排序(稳定的排序)

冒泡:以选出最大为例,如果a[i]>a[i+1],那么交换
思路

每一轮冒泡将会在剩下的(n-i)个元素中产生一个最大或者最少的值,
第一轮i等于0,第二轮i等于1…
而总共需要多少轮冒泡?需要n-1次。每一轮的冒泡需要比较(n-i-1)次即可确定一个极值,所以(n-i-1)就是一轮冒泡要比较的次数

所以时间复杂度为 O(n^2)

 

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])
                std::swap(arr[j],arr[j+1]);
        }
    }
}

 

冒泡优化:

(一)针对大循环

1.如果在本轮排序中,元素有交换,则说明数列无序;如果没有元素交换,说明数列已然有序,直接跳出大循环。

(二)针对小循环 

 2.针对于2,1,3,4,5,6,7而言,进行冒泡1,2进行交换,但是后面都已经有序了,比较2和3,4,5,6,7是无意义的
 

这个问题的关键点在哪里呢?关键在于对数列有序区的界定

按照现有的逻辑,有序区的长度和排序的轮数是相等的。比如第一轮排序过后的有序区长度是1,第二轮排序过后的有序区长度是2 ......

实际上,数列真正的有序区可能会大于这个长度,比如例子中仅仅第二轮,后面5个元素实际都已经属于有序区。因此后面的许多次元素比较是没有意义的。

如何避免这种情况呢?我们可以在每一轮排序的最后,记录下最后一次元素交换的位置,那个位置也就是无序数列的边界,再往后就是有序区了。

 

二、选择排序 (稳定的排序)

(一)算法思想:把数组分成两组,有序序列、无序序列,从头至尾扫描序列,找出最小的一个元素,和无序序列中第一个元素交换,接着从无序序列中剩下的元素中继续这种选择和交换方式,最终得到一个有序序列。

(二)代码


void selectSort(int a[], int len)
{
 
	int minindex, temp;
	for(int i = 0; i<len-1;i++)
	{
	    minindex = i;
	    for(int j = i+1; j<len; j++)
		{
		    if(a[j]<a[minindex])
				minindex = j;
		
		}
		temp = a[i];
		a[i] = a[minindex];
		a[minindex] = temp;
	}
}

(三)、改进

简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。

 

三、 插入排序

(一)、算法思想(稳定的排序)

将数组的第一个数认为是有序数组,从后往前(从前往后)扫描该有序数组,把数组中其余n-1个数,根据数值的大小,插入到有序数组中,直至数组中的所有数有序排列为止。这样的话,n个元素需要进行n-1趟排序!!!

(二)、代码实现 

 void InsertionSort(int *num,int n) 
  {
  	int i = 0;
  	int j = 0;
  	int tmp = 0;
  	for(i = 1;i<n;i++)
  	{
      tmp = num[i];//从待插入组取出第一个元素。 
	  j = i-1; //i-1即为有序组最后一个元素(与待插入元素相邻)的下标 
	  while(j>=0&&tmp<num[j])  //注意判断条件为两个,j>=0对其进行边界限制。第二个为插入判断条件 
	  {
	    num[j+1] = num[j];//若不是合适位置,有序组元素向后移动 
		j--; 
	  }
	  num[j+1] = tmp;//找到合适位置,将元素插入。 
	}
  }

(三)、改进一:二分插入排序

 对于插入排序,如果比较操作的代价比交换操作大的话,可以采用二分查找法(因为已经排序好的,可以使用二分)来减少比较操作的次数,我们称为二分插入排序 

改进二:希尔排序(不稳定的排序)

首先先知道插入排序的特点

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

 希尔排序

希尔排序是把序列按一定间隔分组,对每组使用直接插入排序;随着间隔减小,一直到1,使得整个序列有序

它的基本思想是:对于n个待排序的数列,取一个小于n的整数gap(gap被称为步长)将待排序元素分成若干个组子序列,所有距离为gap的倍数的记录放在同一个组中;然后,对各组内的元素进行直接插入排序。 这一趟排序完成之后,每一个组的元素都是有序的。然后减小gap的值,并重复执行上述的分组和排序。重复这样的操作,当gap=1时,整个数列就是有序的。

代码实现:

void shellSort(int array[],int n){
	int i,j,step;
	for(step = n/2;step>0;step=setp/2){
		for(i=0;i<step;i++){//i是子数组的编号
			for(int j=i+step;j<n;j+=step){//数组下标j,数组步长下标j+step
				if(array[j]<array[j-step]){
					int tmp = array[j];//把数组下标j的值放到temp中
					int k = j- step;
					while(k>=0&&tmp<array[k]){
						array[k+step] = array[k];//把大的值往后移 
						k = k - step;
					}
					array[k+step] = tmp;
				}
			}
		}
	}
} 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值