冒泡排序(Bubble Sort)是一种简单的排序算法。
为什么会叫做冒泡排序呢?这是由于它的算法思想就类似于鱼儿在河里吐泡泡的场景,例如升序排列一列数,它会两两相邻的数据进行比较,如果前者大于后者就交换,重复此番工作直到交换到最后两个数据,第一趟冒泡排序已经完成,最大的数据被冒到数组的最后一个位置,继而缩小冒泡的区间,又从头开始第二趟冒泡,直到次大数被放在倒数第二个位置,以此类推,直到所有数据被冒到合适位置,冒泡排序就算完成。
(1)比较相邻的元素。如果第一个比第二个大,就交换他们两个。
(2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
(3)针对所有的元素重复以上的步骤,除了最后一个。
(4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
//冒泡排序
void BubbleSort(int* arr, int size)
{
if (arr == NULL || size <= 0)
return;
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size - i - 1; j++)
{
if (arr[j]>arr[j + 1])
{
//swap(arr[j], arr[j + 1]);
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
优化版本
void BubbleSort(int* arr, int size)
{
int flag = 0;
for (int i = 0; i < size; i++)
{
flag = 0;
for (int j = 0; j < size - 1 - i; j++)
{
if (arr[j]>arr[j + 1])
{
swap(arr[j], arr[j + 1]);
flag = 1;
}
}
}
if (flag == 0)
{
return;
}
}
(1)时间复杂度:O(n^2)
(2)空间复杂度:O(1)
(3)稳定性:稳定
本质上,快速排序就是冒泡排序的一种改进,冒泡排序是通过每一趟冒泡将最大值(最小值)放到恰当位置。
而快速排序则是每趟排序从待排序区间选一个基准值,将比它小的数据全放在其左边,将比它大的值放在其右边然后递归其左右子区间对其排序,一层层递归下去,某区直到间只剩一个数据时,停止递归,此子区间已经算是有序,继而向其上层区间返回,一层层向上返回,当首次基准值的左右区间均已有序时,整个排序就算完成。
(1)从数列中挑出一个元素,称为 “基准”。
(2)重新排序数列,所有元素比基准值小的摆放在基准左边,所有元素比基准值大的摆在基准的右边(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置,这个称为分区操作。
(3)递归地把小于基准值元素的子数列和大于基准值元素的子数列排序。
目前比较热门的有三种实现快排的算法:左右指针法、挖坑法、前后指针法。
(1)左右指针法
1> 算法的思想步骤
3> 代码实现
//左右指针法
int quicksort1(int* arr, int left, int right)
{
if(arr==NULL||left>right)
return NULL;
int key = right;
while (left < right)
{
while (arr[left] < arr[key])
{
++left;
}
while (arr[right]>arr[key])
{
--right;
}
if (arr[left] != arr[right])
{
//swap(arr[left],arr[right]);
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
swap(arr[left], arr[right]);
return left;
}
(2)挖坑法
1> 算法的思想步骤
用两个指针left和right用来标识区间范围,初始坑设置到key值的地方。我们将key值定义为区间最右边的值。
左指针向右找比key值大的数据,找到后将左指针所指的数据填入坑中,将坑更新为左指针所指位置。现在右指针向左开始找比key小的数据,找到后将右指针所指的数据填入坑中,将坑更新为右指针所指位置。
重复上述过程,左右指针继续走,向中间靠拢直到左右指针相遇,然后将坑处的值值赋值为key。
此时比key值小的数据全部在key的左边,比key大的值全在key的右边。
按照上述同样的方法递归以上key值的左右区间,使之有序后排序完成。
//挖坑法
int quicksort2(int* arr, int left, int right)
{
if(arr==NULL||left>right)
return NULL;
int key = right;
int blank = right;
while (left < right)
{
while (arr[left] <= arr[key])
{
left++;
}
arr[blank] = arr[left];
blank = left;
while (arr[right]>=arr[key])
{
right--;
}
arr[blank] = arr[right];
blank = right;
}
arr[blank] = key;
return blank;
}
(3)前后指针法
1> 算法具体步骤
//前后指针法
int quicksort3(int* arr, int left, int right)
{
if (arr == NULL || left > right)
return NULL;
int prev = left-1;
int cur = left;
int key = arr[right];
while (cur != right)
{
if (arr[cur] < key&&arr[cur] != arr[prev])
{
//swap(arr[cur], key);
int tmp = arr[cur];
arr[cur] = key;
key = tmp;
}
++cur;
}
//swap(arr[++prev], arr[cur]);
int tmp = arr[prev];
arr[prev] = arr[cur];
arr[cur] = tmp;
return prev;
}
(1)三数取中法
1> 基本思想:
2> 代码实现:
//三数取中法
int GetMidIndex(int* arr, int left, int right)
{
int mid = left + ((right - left) >> 1);
if (arr[left] < arr[mid])
{
if (arr[mid] < arr[right])
{
return mid;
}
else if (arr[left]return right;
}
else
{
return left;
}
}
else
{
if (arr[mid] > arr[right])
{
return mid;
}
else if (arr[left] > arr[right])
{
return right;
}
else
{
return left;
}
}
}
(2)
1> 算法思想
2> 代码实现
//小区间优化
void InsertSort(int* arr, int n)
{
if (arr == NULL || n <= 0)
return;
int end = 0;
for (int i = 1; i < n; i++)
{
int tmp = arr[i];
end = i - 1;
while (end >= 0)
{
if (arr[end]>tmp)
{
arr[i + 1] = arr[end];
--end;
}
else
{
break;
}
}
arr[i + 1] = tmp;
}
}
(3)非递归实现快排
1> 算法思想
2> 代码实现
#include
//非递归
void QuickSortNoeR(int* arr, int left, int right)
{
stack<int> s;
s.push(left);
s.push(right);
while (s != empty())
{
int start = s.top();
s.pop();
int finish = s.top();
s.pop();
int div = quickSort1(array, start, finish);
if (start < div - 1)
{
s.push(div - 1);
s.push(start);
}
if (finish > div + 1)
{
s.push(finish);
s.push(div + 1);
}
}
}
(1)时间复杂度:O(NlogN)
快速排序的最好情况:
快速排序的最好情况是每次都划分后左右子序列的大小都相等,其运行的时间就为O(N*1ogN)。
快速排序的最坏情况:
快速排序的最坏的情况就是当分组重复生成一个空序列的时候,这时候其运行时间就变为O(N*N)
(2)空间复杂度:O(logN)
(3)稳定性:不稳定