排序这个东西是我们经常要用到的,下面谈一谈我对排序的理解。
(1)插入排序:插入排序是指先将元素的第一个数当做一个有序的表,然后把第二个元素与第一个元素之间进行比较,插入最开始有序表中,从第二个元素开始,每一个元素都与前面的有序表比较,并加入这个有序表。最后得到一个完全有序的序列。
因为在比较过程中,如果待插入的元素如果与有序表中的某个元素相比,是相等的。那么这个元素就插入在后面,所以插入排序是稳定的。时间复杂度:O(n^2),其他插入排序:2分插入
执行的效果如下图:
// 插入排序
var arr = [1, 4, 5, 7, 3, 2];
function insertSort(arr, length) {
var temp;
for (var i= 0; i < length; i++) {
if (arr[i]>arr[i+1]) {
temp = arr[i+1];
for (var j=i;j>=0&&temp<arr[j];j--){
console.log(123)
arr[j+1]=arr[j];
}
arr[j+1]=temp
}
}
return arr;
}
console.log(insertSort(arr, arr.length))
(2)希尔排序:希尔排序是插入排序的改进,希尔排序按照一个给定的2k,k,k/2...1这样的数来将一堆需要排序的数划分成几个子序列,进行插入排序,然后再将一个基本有序的序列,再进行直接插入排序。
比如说给定一个序列 1 7 6 5 9 2 0 以2划分子序列是 1 5 0,7 9,6 2,对这三个子序列分别进行插入排序,最后再进行直接插入排序。因为在子序列的直接插入过程中,很可能会造成一个数据与另一个子序列中的数据相同,但是却改变了位置,所以希尔排序是一个不稳定的排序。
// 插入排序
void ShellSort(int arr[], int length)
{
int increasement = length;
int i, j, k;
do
{
// 确定分组的增量
increasement = increasement / 3 + 1;
for (i = 0; i < increasement; i++)
{
for (j = i + increasement; j < length; j += increasement)
{
if (arr[j] < arr[j - increasement])
{
int temp = arr[j];
for (k = j - increasement; k >= 0 && temp < arr[k]; k -= increasement)
{
arr[k + increasement] = arr[k];
}
arr[k + increasement] = temp;
}
}
}
} while (increasement > 1);
}
(3)堆排序:堆其实就是一个完全二叉树,而且必须满足根节点必须大于等于子节点或者小于等于孩子节点。
堆排序分成两个过程:
第一个过程是构建最大堆或者最小堆。一个排序后祖先节点就是最大的数或者最小的数。
第二个过程就是排序了,将第一个过程得到祖先节点存入数组中,然后将最后一个叶子节点换到第一个过程得到的祖先节点上,再进行构造最大堆或者最小堆,依次这样直到剩下最后一个叶子节点。
堆排序是不稳定的排序
(4)选择排序:选择排序是指在一个给定的序列中,如果是从小到大排序,首先选择一个最小的数与第一个数交换,然后从第二个数到最后一个数中选择一个最小的数与第二个数交换,依次进行,直到n-1个元素与第n个元素比较。
选择排序是不稳定的排序。
// 选择排序
var arr = [1, 4, 5, 7, 3, 2];
function selectionSort(arr, length) {
var temp;
for (var i= 0; i < length; i++) {
for (var j = i; j < length - 1; j++) {
if (arr[j] > arr[j+1]) {
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
if (arr[j] >= arr[i]) {
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
console.log(arr[j])
}
return arr;
}
console.log(selectionSort(arr, arr.length))
(5)快速排序:这个排序是目前非常火的一个排序,当序列是分布随机的,用快速排序的平均时间是最快的。当排序的数特别大,比如从10000个数中选取最小的十个时用堆排序。
快速排序的思想是给定一个序列,给定两个游标i,j分别指向序列的头和尾(假设以升序排列)。(1)让i和j指向的数进行比较,如果不发生交互,j向后走1,如果一直不发生交换,则j与i重合,则这一趟排序将待排序的序列分成了左右两个部分----i后面的元素都比i大,如果发生交换,则i+1,然后与j比较,如果又发生交换,j-1,直到i,j重合,一趟排序完毕。
快速排序是不稳定的排序。
void QuickSort(int arr[], int start, int end)
{
if (start >= end)
return;
int i = start;
int j = end;
// 基准数
int baseval = arr[start];
while (i < j)
{
// 从右向左找比基准数小的数
while (i < j && arr[j] >= baseval)
{
j--;
}
if (i < j)
{
arr[i] = arr[j];
i++;
}
// 从左向右找比基准数大的数
while (i < j && arr[i] < baseval)
{
i++;
}
if (i < j)
{
arr[j] = arr[i];
j--;
}
}
// 把基准数放到i的位置
arr[i] = baseval;
// 递归
QuickSort(arr, start, i - 1);
QuickSort(arr, i + 1, end);
}
(6)归并排序:把一个序列分成两个或两个以上的多个子序列,分别将各个子序列排列有序,然后合并成一个有序序列。
void MergeSort(int arr[], int start, int end, int * temp)
{
if (start >= end)
return;
int mid = (start + end) / 2;
MergeSort(arr, start, mid, temp);
MergeSort(arr, mid + 1, end, temp);
// 合并两个有序序列
int length = 0; // 表示辅助空间有多少个元素
int i_start = start;
int i_end = mid;
int j_start = mid + 1;
int j_end = end;
while (i_start <= i_end && j_start <= j_end)
{
if (arr[i_start] < arr[j_start])
{
temp[length] = arr[i_start];
length++;
i_start++;
}
else
{
temp[length] = arr[j_start];
length++;
j_start++;
}
}
while (i_start <= i_end)
{
temp[length] = arr[i_start];
i_start++;
length++;
}
while (j_start <= j_end)
{
temp[length] = arr[j_start];
length++;
j_start++;
}
// 把辅助空间的数据放到原空间
for (int i = 0; i < length; i++)
{
arr[start + i] = temp[i];
}
}
(7)冒泡排序
首先从数组的第一个元素开始到数组最后一个元素为止,对数组中相邻的两个元素进行比较,如果位于数组左端的元素大于数组右端的元素,则交换这两个元素在数组中的位置,此时数组最右端的元素即为该数组中所有元素的最大值。接着对该数组剩下的n-1个元素进行冒泡排序,直到整个数组有序排列。算法的时间复杂度为O(n^2)。
附上冒泡排序JavaScript实现方法:
// 冒泡排序
var arr = [1, 4, 5, 7, 3, 2];
function bubbleSort(arr, length) {
var temp;
for (var i= 0; i < length; i++) {
var flag = 0;
for (var j = 0; j < length - i - 1; j++) {
if (arr[j] > arr[j+1]) {
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = 1;
}
}
if (!flag) {
break;
}
}
return arr;
}
console.log(bubbleSort(arr, arr.length))