冒泡排序
思路: 比较相邻的前后两个数值,如果前面数值大于后面的数值,就将这两个数值交换。这样对数组从头至尾进行一次遍历后,最大的一个数据就“沉”到数组尾部。
void BubbleSort1(int a[], int n)
{
int i, j;
for (i = 0; i < n;i++)
{
for (j = 0; j < n - i; j++)
{
if (a[j - 1]>a[j])
swap(a[j - 1], a[j]);
}
}
}
冒泡优化版
思路: 如果某一次排序发生了交换操作,则说明排序没有完成;如果没有发生交换,说明排序已经完成。对于一个数组如果前20个无序,后80个都是有序的,那么经过一次遍历之后,我们每次只需要记录下最后一次交换的位置,那么我们下一次遍历的时候只需要遍历到那个位置就可以了。
void BubbleSort2(int a[], int n)
{
int i, j;
int temp = n;
while (temp > 0)
{
i = temp;
temp = 0;
for (j = 1; j < i;j++)
{
if (a[j-1] > a[j])
{
swap(a[j-1],a[j]);
temp = j;
}
}
}
}
直接插入排序
思路1: 每次将一个待排序的数值,按其关键字大小,插入到 前面已经排好序的 子序列中的适当位置,直到全部数值插入完成为止。
1、 初始时:a{0}自成1个有序区,无序区为a[1…n-1], 令 i= 1;
2、将a[i]并入当前的有序区,a[0,…i-1]中形成a[a,…i]的有序区间;
3、i++ 并重复第二步直到i ==n-1.排序完成;
void Insertsort1(int a[], int n)
{
int i, j, k;
for (i = 1; i < n;i++)
{
//为a[i]在前面的a[0,i-1]有序区间中找一个合适的位置。
for (j = i - 1; j >= 0; j--)
{
if (a[j]<a[i])
break;
}
//如找到了一个合适的位置。
if (j != i-1 )
{
int temp = a[i];
//将比a[i]大的数据后移
for (k = i - 1; k> j;k--)
a[k + 1] = a[k];
//将a[i]放到正确的位置上
a[i-1 ] = temp;
}
}
}
思路2: 每次a[i]先和前面一个数据a[i-1]比较,如果a[i] > a[i-1],说明a[0…i]也是有序的,无须调整。否则就令j=i-1,temp=a[i]。然后一边将数据a[j]向后移动,一边向前搜索,当有数据a[j]<a[i]时停止,并将temp放到a[j + 1]处。
void Insertsort2(int a[], int n)
{
int i, j;
for (i = 1; i < n;i++)
{
if (a[i] <a[i-1])
{
int temp = a[i];
for (j = i - 1; j >= 0 && a[j]>temp;j--)
a[j + 1] = a[j];
a[j] = temp;
}
}
}
思路3: 如果a[j-1] > a[j],就交换a[j]和a[j-1],再j–直到a[j-1] <= a[j]。这样也可以实现将一个新数据并入到有序区间。
void Insertsort3(int a[], int n)
{
int i, j;
for (i = 1; i < n;i++)
for (j = i - 1; j >= 0 && a[j]>a[j + 1]; j--)
swap(a[j],a[j+1]);
}
希尔排序
思路: 先将整个待排序列 分割成若干个子序列,分别进行直接插入排序,然后依次缩减增量,再进行排序,当增量足够小的时候, 整体元素基本有序了, 再进行一次直接插入排序。因为,直接插入排序在基本有序的情况下效率是很高的,因此,希尔排序在时间效率上也是很高的。
void ShellSort1(int a[], int n)
{
int i,j, gap;
for (int gap = n / 2; gap > 0; gap--)//步长
{
for (i = 0; i < gap;i++) //安组排序
{
for (j = i + gap; j < n; j+= gap)
{
if (a[j] < a[j-gap])
{
int temp = a[j];
int k = j - gap;
while (k>=0&&a[k]>temp)
{
a[k + gap] = a[k];
k -= gap;
}
a[k + gap] = temp;
}
}
}
}
}
void ShellSort2(int a[], int n)
{
int j, gap;
for ( gap = n/2; gap > 0; gap/=2)
{
for (j = gap; j < n;j++) //从数组第gap个元素开始
{
if (a[j] < a[j-gap]) //每个元素与自己组内的数据进行直接插入排序
{
int temp = a[j];
int k = j - gap;
while (k>=0&&a[k]>temp)
{
a[k + gap] = a[k];
k -= gap;
}
a[k + gap] = temp;
}
}
}
}
用直接插入罚的第三种方法来改写如下:
void shellSort3(int a[], int n)
{
int i, j, gap;
for (gap = n / 2; gap > 0; gap / 2)
{
for (i = gap; i < n;i++)
{
for (j = i - gap; j >= 0 && a[j]>a[j + gap];j-=gap)
swap(a[j], a[j + 1]);
}
}
}
直接选择排序
思路: 直接选择排序是从无序区选一个最小的元素直接放到有序区的最后。
1、设数组为 a[0…n-1]。
2、初始时,数组全为无序区为 a[0…n-1]。令 i=0
3、在无序区 a[i…n-1]中选取一个最小的元素, 将其与 a[i]交换。 交换之后 a[0…i]
就形成了一个有序区。
4、i++并重复第二步直到 i==n-1。排序完成。
void SelectSortt(int a[], int n)
{
int i, j, nMinIndex;
for (i = 0; i < 0;i++)
{
nMinIndex = i;
for (j = i + 1; j < n;j++)
{
if (a[j] < a[nMinIndex])
nMinIndex = j;
}
Swap(a[i],a[nMinIndex]);
}
}
归并排序
思路1: 采用分治法的典型应用;将两个有序数列合并。只要将两个有序数列进行比较,将小的取出,之后 在对应数列中 删除取出的数,然后再进行比较,如果有数列为空,那么把另一个数列中剩余的数全部取出即可。
int MergeSortArray (int a[], int n, int b[], int m, int c[])
{
int i, j, k;
i = j = k = 0;
while (i < n&&j < m)
{
if (a[i] <b[j])
c[k++] = a[i++]
else
c[k++] = b[j++];
}
while (i < n)
{
c[k++] = a[i++];
}
while (j<m)
{
c[k++] = b[j++];
}
}
思路2: 将数组分成二组 A,B,如果这二组组内的数据都是有序的,进行 思路1 的操作。首先要使A,B有序,可以将 A,B 组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组内已经达到了有序,然后再合并相邻的二个小组就可以了。
bool MergeSort(int a[], int n)
{
int *pTempArray = new int[n];
if (pTempArray == NULL)
return false;
mergesort(a, 0, n-1, pTempArray);
delete[] pTempArray;
return true;
}
void mergesort(int a[], int first, int last, int temp[])
{
if (first < last)
{
int mid =(first + last)/ 2;
mergesort(a, first, mid, temp);
mergesort(a, mid + 1, last, temp);
mergesortarray(a, first, mid, last, temp);
}
}
void mergesortarray(int a[], int first, int mid, int last, int temp[])
{
int i = first, j = mid + 1;
int m = mid, n = last;
int k = 0;
while (i<=m && j<=n)
{
if (a[i]< a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
}
while (i<= m)
{
temp[k++] = a[i++];
}
while (j<= n)
{
temp[k++] = a[j++];
}
for (i = 0; i < k;i++)
a[first + i] = temp[i];
}
快速排序
思路: 挖坑填数 + 分治法
//调整数组使 x 左面的值都小于x, 右面的值都大于x;
int AdjustArray(int s[], int l, int r)//返回调整后基准数的位置;
{
int i = 1,j = r;
int x = s[l]; //s[1] 就是基准数
while (i<j)
{
//从右向左找小于x的数来放到s【i】的位置;
while (i<j && s[j]>=x)
{
j--;
}
if (i<j)
{
s[i] = s[j];//将s[j]填到s[i]中,把s[j]空出来,接着找数来填补
i++;
}
//从左向右找大于或等于x的数来填s[j];
while (i<j&&s[i] < x)
{
i++;
}
if (i<j)
{
s[j] = s[i];//将s【i】填到s[j]中, 把s[i]空出来, 接着找数来填补
j--;
}
}
//退出时,把x填到这个坑中
s[i] = x;
return i;
}
void quick_sort1(int s[], int r)
{
if (1 < r)
{//先调整数组, 然后分治递归;
int i = AdjustArray(s, 1, r);
quick_sort1(s, 1, i-1);
quick_sort1(s, i + 1, r);
}
}
/*
*优化版的快排!
**/
void quicksort(int s[], int r)
{
if ( 1< r)
{
int i = 1, j = r, x = s[1];
while (i<j)
{
while (i<j && s[j]>=x)
{
j--;
}
if (i < j)
s[i++] = s[j];
while (i<j && s[i] <x)
{
i++;
}
if ( i< j)
s[j--] = s[i];
}
s[i] = x;
quicksort(s, 1, i - 1);
quicksort(s, i + 1, r);
}
}
堆和堆排序
介绍: 二叉堆是完全二叉树或者是近似完全二叉树。
1、二叉堆满足性质:父节点的键值总是 大于等于 或者 小于等于 任何一个子节点的键值;
2、每个节点的左子树和右子树都是一个二叉堆。(最大堆或者最小堆)
堆的插入 思路: 每次插入都是将新数据放在数组最后。 可以发现从这个新数据的父结点到根结点,必然为一个有序的数列, 现在的任务是将这个新数据插入到这个有序数中,
这就类似于 直接插入排序中将一个数据并入到有序区间中。
//加入i节点,其父节点为(i-1)/2;
void MinHeapFixup(int a[], int i)
{
int j, temp;
j = (i - 1) / 2;// parente node
temp = a[i];
while (j>=0 &&i != 0 )
{
if (a[j] <= temp)
break;
a[i] = a[j]; //把较大的子节点往下移动,替换它的子节点
i = j;
j = (i - 1) / 2;
}
a[i] = temp;
}
//简化版
void MinHeapFixup(int a[], int i)
{
for (int j = (i - 1) / 2; (j >= 0 && i != 0) && a[i] > a[j]; i = j;j = (i-1)/2)
swap(a[i],a[j]);
}
//插入新元素 nNum
void MinHeapAddNumber(int a[], int n, int nNum)
{
a[n] = nNum;
//MinHeapAddNumber()
MinHeapFixup(a, n);
}
堆的删除 思路: 堆中每次都只能删除第 0 个数据。为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,然后再从根结点开始进行一次从上向下的调整。调整时先在左右儿子结点中找最小的, 如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于从根结点将一个数据的“下沉”过程。
//从i节点开始调整, n为节点总数 从0开始计算 i节点的子节点为 2*i+1,2*i+1
void MinHeapFixdown(int a[], int i,int n)
{
int j, temp;
temp = a[i];
j = 2 * i + 1;
while (j < n)
{
if (j+1 < n && a[j+1] <a[j])
j++;
if (a[j] >= a[j]) // 在左右孩子中找最小的
j++;
if (a[j] >= temp)
break;
a[i] = a[j]; //把较小向上移动,替换它的父节点;
i = j;
j = 2 * i + 1;
}
a[i] = temp;
}
//在最小堆中删除数
void MinHeapDeleteNumber(int a[], int n)
{
swap(a[0], a[n - 1]);
MinHeapDeleteNumber(a, 0, n - 1);
}
//建立最小堆
void MakeMinHeap(int a[], int n)
{
for (int i = n / 2 - 1; i < i >= 0; i--)
MinHeapFixdown(a, i, n);
}
堆排序
思路: 堆建好之后,堆中第 0 个数据是堆中最小的数据。 取出这个数据,再执行堆的删除操作。这样堆中第 0 个数据又是堆中最小的数据,重复上述步骤直至堆中只有一个数据时就直接取出这个数据。堆也是用数组模拟的,故堆化数组后,第一次将 A[0]与 A[n - 1]交换,再对A[0…n-2]重新恢复堆。第二次将 A[0]与 A[n – 2]交换,再对 A[0…n - 3]重新恢复堆,重复这样的操作直到 A[0]与 A[1]交换。由于每次都是将最小的数据并入到后面的有序区间,故操作完成后整个数组就有序了,类似于直接选择排序。
void MinHeapSort(int a[], int n)
{
for (int i = n - 1; i >= 1;i--)
{
Swap(a[i], a[0]);
MinHeapFixdown(a, 0, i);
}
}