排序算法的稳定性:
如有13, 2, 44, 56, 56*, 98, 10这个序列,
稳定排序后:2, 10, 13, 44, 56, 56*, 98, 这可以做到两个相等的元素(复杂类型中为KEY)一定不互换
但不稳定排序后的结果可能是:2, 10, 13, 44, 56*, 56, 98, 这样并不能一定保证相等的两个元素的位置不互换,\
在基本类型中稳定或不稳定是无关紧要的,
但是在复杂类型中, 根据关键字排序, 使用不稳定排序, 可能会让两个相同关键字的元素交换位置, 这可能并不是开发者所设想的
冒泡排序:
void BubleSort(int arr[], int len)
{//冒泡排序, 最古老的排序, 效率最低的排序
//思想 : 通过比较相临的元素, 如果前一个元素大于后一个元素, 就交换它们
//这样每次就把当前比较中的元素, 最大的元素放到最后, 达到排序的目的
for (int i = 0; i < len - 1; i++)
{//外层循环次数为数组长度-1
for (int j = 0; j < len - 1 - i; j++)
{//内层循环每次递减, 因为最大的元素始终在最后, 没必要在比较最大的元素
if (arr[j] > arr[j + 1])
{//如果前一个比较大,则交换相临的两个元素
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
}
冒泡排序进阶版->快速排序
void QuickSort(int arr[], int left/*最左边元素下标*/, int right/*最右边元素下标*/)
{//快速排序
//思想 : 采用分治算法, 找到一个关键字, 把小于关键字的元素放在关键字的左边
//把大于关键字的放在关键字的右边,然后,递归,把左边的再次使用快排,右边的也
//使用快排,最后完成排序
if (left >= right)
{//左下标和右下标重叠时, 返回
return;
}
int i = left; //i从左往右表示下标
int j = right + 1; //j从右往从表示下标, ***注 : 这里j要超尾, j超尾, 但是right必需是最右边元素的下标***
int varKey = arr[left]; //最左边的元素设定为关键字
while (true)
{//外层循环, 把关键字放到正确位置后, 退出循环
do
{
i++;
} while (arr[i] < varKey);
//从左往右找, 当找到比关键字小的, 退出
do
{
j--;
} while (arr[j] > varKey); //从右往左找, 当找到比关键字, 退出
if (i >= j)
{//当i与j重叠时,退出外层循环
break;
}
//相换两个元素
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
arr[left] = arr[j];
arr[j] = varKey; //最后把关键字放到左组和右组的中间, 同时也以经确定了关键字的正确位置
QuickSort(arr, left, j - 1); //把关键字左边的组进行快排
QuickSort(arr, j + 1, right); //所关键字右边的组进行快排
}
直接插入排序
void InsertionSort(int arr[], int len)
{//直接插入排序
//思想 : 首先让外层的当前元素为待排序元素(temp),
//然后和有序列表中以从后往前的顺序进行比较
//最后将待排序元素插入到以经排好序的列表中
//一开始有序列表长度为1, 然后为2....直到最后长度为整个数组的长度
for (int i = 1; i < len; i++)
{//外层循环为数组长度, 这里从第二个元素开始插入, 因为一个元素不存在排序
int temp = arr[i]; //设置待排序元素
int j = i - 1;
while (j >= 0 && arr[j] > temp)
{//当j>0时, 并且待排元素小于当前比较元素, 则将当前元素后移, 然后继续同有序列表中前面的元素比较
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
}
直接插入排序进阶版->二分插入排序
void BinInsertionSort(int arr[], int len)
{//二分插入排序
//思想 : 首先找到有序列表中中间的元素,
//arr[mid] mid = (left(有序列表的第一个元素下标) + right(有序列表的最后一个元素下标)) / 2
//将待插入元素于中间元素比较, 如果待插元素较大, 则应插在中间元素右边, 则将left 标为mid的后面,
//再继续二分比较, 直到找到left的正确位置,然后将left元素后面的元素后移, 最后插入left
int left/*有序表中第一个元素下标*/, right/*有序表中最后的元素下标*/, mid/*中间元素下标*/;
for (int i = 1; i < len; i++)
{//外层循环为数组长度, 这里从第二个元素开始插入, 因为一个元素不存在排序
int temp = arr[i]; //取待插入元素
left = 0;
right = i - 1;
while (left <= right)
{//确定left下标的循环, 不停的将当前段截成左右两段
mid = (left + right) / 2; //mid为当前段的中间元素下标
if (temp < arr[mid])
{//如果待插元素比中间元素小, 将left 到 right段设置为原先mid段的左边
right = mid - 1;
}
else
{//否则, 将left到right段设置为原先mid段的右边
left = mid + 1;
}
}
for (int j = i - 1; j >= left; j--)
{//将有序列表中left下标后的元素后移, 这里操作的就是整个有序列表了.
arr[j + 1] = arr[j];
}
arr[left] = temp; //插入待排序元素
}
}
直接插入排序终级版->希尔排序:
设置一个步长,然后将在步长内的元素分组, 然后在分组内进行直接插入排序
void ShellSort(int arr[], int len)
{//希尔排序
//恩想 : 设定一个步长, 然后将相隔该步长的元素分到一组,
//进行比较,符合条件就交换,然后继续与该组前面的元素进行比较,
//不符合条件就停止该组的比较
int d; //步长, 不断减半, 直到为1
int j;
for (d = len / 2; d >= 1; d = d / 2)
{//外层为步长/2直到为1的循环次数
for (int i = d; i < len; i++)
{//将i设置为当前步长, 并定为元素下标
int temp = arr[i]; //设置待插入元素, 注意是从后入前的顺序比较, 待插入元素为该步长最后元素
for (j = i - d; (j >= 0) && (arr[j] > temp); j = j - d)
{//设置当前比较元素, 当该组中前面的元素比较大时, 把较小元素插到该分组的前面
arr[j + d] = arr[j]; //后移的位置为步长
}
arr[j + d] = temp;
}
}
}
选择排序:
void SelectionSort(int arr[], int len)
{//选择排序
//每次选出最小的元素与数组最前端的元素进行排序
int min = 0; //最小元素的下标
for (int i = 0; i < len - 1; i++)
{//外层循环为数组长度-1
min = i; //min为当前元素下标
for (int j = i + 1; j < len ; j++)
{//内层循环只在没有被选为最小元素的元素中进行选择
if (arr[j] < arr[min])
{//当前元素比"临时最小元素"小, 获得该较小元素下标
min = j;
}
}
if (min != i)
{//把当前元素(arr[i])与arr[min]互换, 因为这时的i肯定是在数组的前最前端元素
int temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
}
}