一、冒泡排序(稳定的排序)
冒泡:以选出最大为例,如果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;
}
}
}
}
}