目录
冒泡排序
冒泡排序是依次比较数组中相邻的两个数,将两个数中较大的数向后移动,循环一次下来,整个数组中最大的是数一定在最后面,依次循环,第二次循环,数组中次最大数一定在次最后面,如此反复...
图解如下:
代码如下:
未优化版本:
void BubbleSort(int* a, int n)
{
for (int i = 0; i < n; i++)//每趟都会只会把一个最大的数放到后面,因此需要循环n次
{
for (int j = 0; j < n - i - 1; j++)
{
//两两比较,大的后移(交换到右边),以此类推,直到最后一个
if (a[j] > a[j+1])
{
swap(a[j], a[j+1]);
}
}
}
}
优化版本:
我们想一下,按照上面那个方法,如果只排序了一趟,数组里的元素就已经全部有序了,但是他还得继续循环进行,直到把最后一趟循环完毕.这样是不是太浪费时间了.
我们有没有办法解决这个问题呢?
当然有,我们可以做一个标记,如果发生了交换操作,则将标记更改;如果没有,则直接循环结束.排序完毕.
具体代码如下:
void BubbleSort(int* a, int n)
{
int exchange = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (a[j] > a[j+1])
{
swap(a[j], a[j+1]);
exchange = 1;
}
}
if (exchange == 0)//如果exchange=0,如果此时没有发生交换,既已经排序好了,退出循环
{
break;
}
}
}
分析:
最坏是每次循环都执行,因此时间复杂度为O(N^2).
没有开辟额外的空间,因此空间复杂度为O(1).
选择排序
每次从待排序的元素中选出最小(或最大)的一个元素,然后和数组左端的元素进行交换,直到全部排列完成.
图解如下:
未优化版本:
void SelectSort(int* a, int n)
{
int i = 0, j = 0;
int min = 0;
for (int i = 0; i < n; i++)
{
min = i;//先假设最小值的下标为i
for (int j = i + 1 ; j < n; j++)
{
if (a[j] < a[min])//若数组中有的值比最小值还小,则将min下标替换为此下标
{
min = j;
}
}
if (min != i)//若不相等,说明此时min下标发生了变化,交换此下标的值和 下标i位置的值;若相等,则没有发生变化,不必交换.
{
swap(a[i], a[min]);
}
}
}
但是我们转念一想,既然可以每次选出最小值,为什么不同时选出最大值放在数组的右端呢,这样效率不就是原来的2倍了吗?说做就做,我们试着实现它.但同时有一些问题,我们后面说.
优化版本:
void SelectSort(int* a, int n)
{
int begin = 0, end = n - 1;
while (begin < end)
{
int mini = begin, maxi = begin;//先将最小值,最大值下标初始化为begin.
for (int i = begin; i <= end; i++)
{
if (a[i] < a[mini])
{
mini = i;//如果有的值比最小值小,则将最小值下标=那个值的下标
}
if (a[i] > a[maxi])
{
maxi = i;//如果有的值比最大值大,则将最大值下标=那个值的下标
}
}
swap(a[mini], a[begin]);//交换最小值和未排序的最左端的值
if (begin == maxi)//如果begin的位置恰好是最大值的位置,由于上一条语句mini和begin已经交换,说明此时最大值下标已经不是begin,而是和begin交换后的mini位置,所以将maxi = mini.
{
maxi = mini;
}
swap(a[maxi], a[end]);//一切完成后,交换最大值和未排序的最右端的下标
begin++;
end--;
}
}
这是选择排序的算法.
下面来看插入排序
插入排序
先来看直接插入排序.
我们平常玩扑克牌的时候,每次摸牌,我们总会把摸到的牌插入到合适的位置,等摸完牌以后,我们的牌的顺序也就好了.
插入排序也是这个道理.
原理:当插入第i个元素时,前面的i-1个元素已经排序完成.
此时将arr[i]分别与arr[i-1],arr[i-2]...比较,每比较一次,将被比较的那个元素向后移动一位,最后找到插入位置
找到插入位置之后,将这个位置元素后移,留出位置,将元素插入.
图解如下:
思路是:
我们不知道数组是否有序,所以通过控制下标的方式进行排序
开始让end的下标为0,每次保存end+1的值x,让x与x前面的值进行比较,判断x的值是否小于某个值,如果小于则end--,大于的话说明此时这个位置之后正是x的插入位置.将x插入即可.
代码如下:
void InsertSort(int* a, int n)
{
for (int i = 0; i < n - 1; i++)
{
int end = i;
int x = a[end + 1];//要插入的值保存到x
while (end >= 0)//若end<0,则说明被插入的元素在第一位,直接a[end+1]即a[0] = x即可
{
if (x < a[end])//x小于a[end],则继续向前查找,直到大于
{
a[end + 1] = a[end];//如果不符合条件,则把这个位置向后移.
end--;
}
else
{
break;//x >= a[end],直接break,将x插入到这个位置end+1这个位置,因为上一次end--
}
}
a[end + 1] = x;
}
}
最差情况,每次插入要向前移动n-1个元素,所以时间复杂度为O(N)=1+2+3+4+....n-1 = n(n-1)/2=O(N^2)
没有创建额外的空间,所以时间复杂度为O(1)
这就是冒泡排序、选择排序、和插入排序的全部内容了.
下一节我们将讲解希尔排序和归并排序.
排序重要的是看懂思想,理解代码,就会变得很简单啦.