排序算法

数据结构第八章排序算法实现

插入排序

直接插入排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
void InsertSort(int A[],int n)
{
  int j,j,temp;
  for(int i = 1;i < n;i++)//所有i之前的元素为有序序列,初始第一个元素自己为有序序列
  {
    if(A[i-1] > A[i])//待排序的i如果大于之前的元素(有序序列最大值),不用进入处理,否则进入之前的序列进行对比处理
    {
      temp = A[i];//临时保存第i元素的值,因为要在前面进行插入,所以有序序列需要进行后移
      for(j = i - 1;j>=0&&A[j]>temp;j--)A[j+1] = A[j];//依次向前比较,如果temp值小,需要向前比较,序列后移,跳出循环时temp要么需要在0位置插入(j=-1),要么此时的j对应的元素已经不大于temp,所以需要temp插入j+1的位置
      A[j+1] = temp;
    }
  }
}

希尔排序

通过设置增量,将数组分成不同序列,子序列内部采用直接插入,增量不断减小,直到为1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void ShellSort(int A[],int n)
{
  int d,i,j,temp;//d为增量
  for(d=n/2;d>=1;d/=2)//增量初始设为数组长度一半,每次循环结束取其一半
  {
    for(i = d;i<n;i++)//子序列内部采用直接插入排序
    {
      if(A[i]<A[i-d])
      {
        temp = A[i];//临时保存需要插入的值,因为此位置会被前面序列后跳取代
        for(j = i-d;j>=0&&A[j]>temp;j-=d)A[j+d]=A[j];//j每次向前走d个单位,若大于待插入值,值后跳
        A[j+d] = temp;//出来时此位置后+d的位置需要被插入
      }
    }
  }
}
//这个版本可能更直观,先把某一子序列整体排完,才切换下一个子序列
void ShellSort(int A[], int n)
{
    int d, start, i, j, temp;

    // d 为增量
    for (d = n / 2; d >= 1; d /= 2)
    {
        // start 表示每个子序列的起点
        // 一共有 d 个子序列:0,1,2,...,d-1
        for (start = 0; start < d; start++)
        {
            // 对 A[start], A[start+d], A[start+2d]... 做直接插入排序
            for (i = start + d; i < n; i += d)
            {
                if (A[i] < A[i - d])
                {
                    temp = A[i];

                    for (j = i - d; j >= 0 && A[j] > temp; j -= d)
                    {
                        A[j + d] = A[j];
                    }

                    A[j + d] = temp;
                }
            }
        }
    }
}

交换排序

冒泡排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20

void BubbleSort(int A[],int n)
{
  int i,j,temp,flag;
  for(i = 0;i<n-1;i++)//一共需要n-1轮
  {
    flag = 0;
    for(j = 0;j<n-1-i;j++)//每轮需要n-1次再减去已经排好的个数(也等于当前轮数)
    {
      if(A[j]>A[j+1])
      {
        temp = A[j];
        A[j] = A[j+1];
        A[j+1] = temp;
        flag = 1;//如果发生了交换,说明序列还未有序
      }
    }
    if(!flag)break;//如果次轮没有交换,此时flag为0,说明已经有序,可以跳出循环
  }
}

快速排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int Partition(int A[],int low,int high)
{
  int pivot = A[low];//让第一个元素为中间元素
  while(low<high)
  {
    
    while(low<high&&A[high]>=pivot)high--;//从后找到比中间元素小的元素
    A[low] = A[high];//换到左边low的位置
    while(low<high&&A[low]<=pivot)low++;//从前找到比中间元素大的元素
    A[high] = A[low];//换到右边high的位置
  }
  A[low] = pivot;//最后low=high,这个位置就是中间元素的位置
  return low;//返回这个位置
}
void QuickSort(int A[],int low,int high)
{
  if(low < high)
  {
    int pivotposition = Partition(A,low,high);//找个中间元素位置
    QuickSort(A,low,pivotposition-1);//递归左边
    QuickSort(A,pivotposition+1,high);//递归右边
  }
}

选择排序

简单选择排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
void SelectSort(int A[],int n)
{
  int i,j,temp,minIndex;
  for(i = 0;i<n-1;i++)
  {
    minIndex = i;//当前最小值下标为i
    for(j = i+1;j<n;j++)
    {
      if(A[j]<A[minIndex])minIndex = j;//如果当前元素小于最小值,更新最小值元素下标
    }
    if(minIndex!=i)//当前最小值有变化,交换元素
    {
      temp = A[i];
      A[i] = A[minIndex];
      A[minIndex] = temp;
    }
  }
}

堆排序

采用王道书写法,从下标1开始,0位置用来暂存子树根节点 这样分支节点<=n/2,左孩子为2i,右孩子为2i+1;

思路

将数组调整为大顶堆,任意根节点大于其左右子树,这样保证调整后1号位置为最大值,然后与最后元素交换,保证递增, 此时大顶堆树结构被破坏,将后面排好序的元素去除重新调整堆

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

void HeapAdjust(int A[],int k,int len)
{
  A[0] = A[k];
  int i;
  for(i = 2*k;i<=len;i*=2)//左孩子为2k,右孩子为2k+1
  {
    if(i<len&&A[i]<A[i+1])i++;//比较左右子树谁值更大;
    if(A[0]>=A[i])break;
//大顶堆 → 小顶堆:
//if(i<len&&A[i]>A[i+1])
//if(A[0]<=A[i])break;
    else{
      A[k] = A[i];
      k=i;//发生调整,从此开始,继续向下调整
    }
  }
  A[k] = A[0];//最后找到原来根的位置
}

void BuildBigHeap(int A[],int len)//分支节点为<=n/2(因为数据从1开始)
{
  int i;
  for(i = len/2;i>0;i--)
  {
    HeapAdjust(A,i,len);
  }
}

void HeapSort(int A[],int len)
{
  BuildBigHeap(A,len);//初始建堆
  for(int i = len;i>1;i--)//执行n-1次
  {
    swap(A[1],A[i]);//每次把最后的元素和根节点元素互换,保证最大的在最后
    HeapAdjust(A,1,i-1);//从新调整1到未排序元素之间的树;
  }
}
//大顶堆 → 小顶堆:
//if(i<len&&A[i]<A[i+1]) → if(i<len&&A[i]>A[i+1])
//if(A[0]>=A[i])break; → if(A[0]<=A[i])break;

归并排序

思路

通过辅助数组,将待排序两个序列复制到辅助数组,通过两个指针从两个字序列开始位置向后对比,递增排序则,较小值覆盖原数组位置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int *B = (int *)malloc(n*sizeof(int));

void Merge(int A[],int low,int mid,int high)
{
  int i,j,k;//i,j分别指向辅助数组里第一,二个子序列,k指向原来数组待插入位置
  for(k = low;k<=high;k++)
  {
    B[k] = A[k];
  }//先通过k复制元素到辅助数组
  for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++)//选取两个子序列小值放入原数组
  {
    if(B[i]<=B[j])A[k]=A[i++];
    else A[k] = A[j++];
  }
  while(i<=mid)A[k++]=A[i++];//某一个子序列完毕,将剩余一个数组放入原数组,只会循环一个
  while(j<=high)A[k++]=A[j++];
}

void MergeSort(int A[],int low,int high)
{
  if(low<high)
  {
    int mid = (low+high)/2;//当前中间元素
    //int mid = low + (high - low) / 2;防溢出
    MergeSort(A,low,mid);//递归进去排左边
    MergeSort(A,mid+1,high);//递归进去排右边
    Merge(A,low,mid,high);//归并两个子序列
    //理解递归思想,第一次进去,会先层层进去到前两个元素归并
  }
}

计数排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void CountSort(int A[],int B[],int n,int k)//B为输出数组,n为A的长度,k为A[]的取值范围
{
  int i,c[k];
  for(i=0;i<n;i++)
  {
    C[A[i]]++;//统计出现次数
  }
  for(i = 1;i<k;i++)
  {
    C[i] = C[i] + C[i-1];//从前向后统计各位置序号位置
  }
  for(i = n-1;i>=0;i--)
  {
    C[A[i]] = C[A[i]]-1;//先得到放入位置
    B[C[A[i]]] = A[i];
  }
}

相关内容