1.冒泡排序
起泡排序,别名“冒泡排序”,该算法的核⼼思想是将⽆序表中的所有记录,通过两两⽐较关键字, 得出升序序列或者降序序列。
例如,对⽆序表{49,38,65,97,76,13,27,49}进⾏升序排序的具体实现过程如图所示:
上图所示是对⽆序表的第⼀次起泡排序,最终将⽆序表中的最⼤值 97 找到并存储在表的最后⼀个位置。
由于 97 已经判断为最⼤值,所以第⼆次冒泡排序时就需要找出除 97 之外的⽆序表中的最⼤值,⽐ 较过程和第⼀次完全相同。
通过⼀趟趟的⽐较,⼀个个的“最⼤值”被找到并移动到相应位置,直到检测到表中数据已经有序,或者⽐较次数等同于表中含有记录的个数,排序结束,这就是起泡排序。
2. 关于冒泡的优化
基本的冒泡排序的实现⽅式,就是两个for循环,持续⽐较和交换。这种实现⽅式有⼀个明显的弊 端,就是不论数组是否有序,两层 for 循环都要执⾏⼀遍,⽽我们是希望数组有序的时候,仅进⾏⼀轮判断,或者⼀轮都不进⾏(当然不判断,排序算法是不能知道数组是否有序的)。
⼀次优化:
这⾥我们增加了⼀个标识数组是否有序 ,当冒泡排序过程中没有交换操作时, swapped = false , 也意味着数组有序;否则数组⽆序继续进⾏冒泡排序。不要⼩看这个变量奥,因为这个变量, 当数组有序的时候,冒泡排序的时间复杂度将降⾄ (因为其只需要执⾏⼀遍内层的 for 循环就可以结束冒泡排序),没有这个变量,数组有序也需要很大的时间复杂度。
二次优化:
确定出已经有序部分和⽆序部分的边界
//bubbleSort.h
#ifndef SORT_BUBBLESORT_H
#define SORT_BUBBLESORT_H
#include "../sortHelper.h"
void bubbleSortV1(SortTable* table);
void bubbleSortV2(SortTable* table);
void bubbleSortV3(SortTable* table);
#endif //SORT_BUBBLESORT_H
//bubbleSort.c
#include "bubbleSort.h"
/*找最大值,把最大的放到了右边
* j[0,n-1] i>i+1 swap
* j[0,n-2] i>i+1 swap
* */
void bubbleSortV1(SortTable* table)
{
for(int i=0;i<table->length;++i)
{
for(int j=0;j<table->length-i-1;++j)
{
if(table->data[j].key>table->data[j+1].key)
{
swapElement(&table->data[j],&table->data[j+1]);
}
}
}
}
//引入是否有序标记,当发现某一轮冒泡时有序,就退出循环
void bubbleSortV2(SortTable* table)
{
for(int i=0;i<table->length;++i)
{
int isSorted=0;
for(int j=0;j<table->length-i-1;++j)
{
if(table->data[j].key>table->data[j+1].key)
{
swapElement(&table->data[j],&table->data[j+1]);
isSorted=1;
}
}
if(isSorted==0)break;
}
}
/* 引入newIndex,标记最后一次交换的位置,下次冒泡排序是,只需要遍历到这个newIndex位置处 */
void bubbleSortV3(SortTable* table)
{
int newIdex;
int n=table->length;
do{
newIdex=0;
for(int i=0;i<n-1;++i)
{
if(table->data[i].key>table->data[i+1].key)
{
swapElement(&table->data[i],&table->data[i+1]);
newIdex=i+1;
}
n=newIdex;
}
}while(newIdex>0);
}
3. 快速排序
快速排序算法是在起泡排序的基础上进⾏改进的⼀种算法,其实现的基本思想是:通过⼀次排序将 整个⽆序表分成相互独⽴的两部分,其中⼀部分中的数据都⽐另⼀部分中包含的数据的值⼩,然后继续 沿⽤此⽅法分别对两部分进⾏同样的操作,直到每⼀个⼩部分不可再分,所得到的整个序列就成为了有序序列。
//quickSort.h
#ifndef SORT_QUICKSORT_H
#define SORT_QUICKSORT_H
#include "../sortHelper.h"
/* 快速排序:
* 冒泡排序在每一轮中,只把1个元素冒泡到数组的一端
* 1. 快速排序,在每一轮挑选一个基准元素,让其他比它大的元素移动到一边,比它小的移动到另外一边
* 从而把表元素拆分成两个部分。
* 2. 基准元素的选择
* 随机选择一个元素作为基准元素,并让基准元素和第一个元素进行交换
* 3. 元素交换
* 分为双边循环法和单边循环法
* */
void quickSortV1(SortTable *table);
void quickSortV2(SortTable *table);
#endif //SORT_QUICKSORT_H
#include <stdlib.h>
#include <time.h>
#include "quickSort.h"
/* 单边循环法,设置一个mark标记,表示小于基准元素的区域边界
* 1. 从基准元素的下一个位置开始遍历数组
* 2. 如果遍历到的元素大于基准元素,就继续向后遍历
* 3. 如果遍历到的元素小于基准元素
* 3.1 把mark++
* 3.2 把当前mark的值(大于基准元素)和找出来小的值交换
* 4. 最后把mark和基准元素交换位置
* */
static int partitionSingle(SortTable *table, int startIndex, int endIndex) {
keyType tmp = table->data[startIndex].key;
int mark = startIndex;
for (int i = startIndex + 1; i <= endIndex; ++i) {
if (table->data[i].key < tmp) {
mark++;
swapElement(&table->data[mark], &table->data[i]);
}
}
swapElement(&table->data[mark], &table->data[startIndex]);
return mark;
}
/* 双边循环法
* 1. 设置左、右标记,基准点认为最左边的元素
* 2. 先从右标记开始,让right标记指向的元素和基准元素比较,如果大于pivot,则左移
* 如果小,则停止right标记移动,切换到left标记移动
* 3. 在left标记中,如果小于基准,则右移,如果大于基准,那么left标记就停止移动
* 4. 此时交换左右的值
* 5. 接下来继续第二轮循环
* 6. 最后,当left和right相等时,基准就找到了,那么将基准值放入这个位置
* */
static int partitionDouble(SortTable *table, int startIndex, int endIndex) {
int pivot = startIndex;
int left = startIndex;
int right = endIndex;
srand(time(NULL) + 1);
swapElement(&table->data[startIndex], &table->data[rand() % (endIndex - startIndex) + startIndex]);
while (left != right) {
while (left < right && table->data[right].key > table->data[pivot].key) right--;
while (left < right && table->data[left].key <= table->data[pivot].key) left++;
if (left < right) {
swapElement(&table->data[right], &table->data[left]);
}
}
swapElement(&table->data[pivot], &table->data[left]);
return left;
}
static void quickSort1(SortTable *table, int startIndex, int endIndex) {
if (startIndex >= endIndex) {
return;
}
int pivot = partitionDouble(table, startIndex, endIndex);
quickSort1(table, startIndex, pivot - 1);
quickSort1(table, pivot + 1, endIndex);
}
static void quickSort2(SortTable *table, int startIndex, int endIndex) {
if (startIndex >= endIndex) {
return;
}
int pivot = partitionSingle(table, startIndex, endIndex);
quickSort1(table, startIndex, pivot - 1);
quickSort1(table, pivot + 1, endIndex);
}
void quickSortV1(SortTable *table) {
quickSort1(table, 0, table->length - 1);
}
void quickSortV2(SortTable *table) {
quickSort2(table, 0, table->length - 1);
}
#include "bubbleSort.h"
#include "quickSort.h"
int main() {
int n = 10000;
SortTable *table1 = generateRandomArray(n, 0, n);
SortTable *table2 = copySortTable(table1);
SortTable *table3 = copySortTable(table1);
testSort("quick Sort V1", quickSortV1, table1);
//testSort("quick Sort V2", quickSortV2, table2);
testSort("Bubble Sort V3", bubbleSortV3, table3);
releaseSortTable(table1);
releaseSortTable(table2);
releaseSortTable(table3);
return 0;
}