DataStructure-9-排序技术

9.1 排序技术

9.1.1 概念



9.1.1  正序,逆序

     若待排记录序列中的记录已按关键码排好序,称此记录序列为正序;若待排序序列中记录的排列顺序与排好序的顺序正好相反,称此记录序列为逆序(反序).

9.1.2 趟

    在排序过程中,将待排序的记录序列扫描一遍称为一趟

9.1.4 排序算法的稳定性

     

9.1.5 排序的分类

     

9.1.6 排序算法的性能

  


插入排序

9.2 直接插入排序

     9.2.1 基本思想:

         依次将待排序序列中的每一个记录插入到一个已排好序的序列中,直到全部记录都排好序.

    9.2.2 具体排序过程:

        ①将整个待排序的记录序列划分成有序区和无序区,初始时有序区为待排序序列中的第一个记录,

         无序区包括所有剩余待排序的记录.

        ②将无序区的第一个记录插入到有序区的合适位置中,从而使无序区减少一个记录,有序区增加一个记录.

        ③重复执行②,直到无序区中没有记录为止.

    9.2.3 伪代码:

          1. 0号下标作为哨兵存在,从1号下标元素开始,该元素可以认为已经被排好序.

          2. 从无序区中取出一个元素,直到取完为止.

          3. 将其赋值给哨兵,此后直接使用哨兵进行比较.

          4. 将哨兵与有序区中元素从后向前依次比较.

               4.1若哨兵小于有序区元素,则有序区元素后移一位.

          5. 直到哨兵大于或等于有序区元素的位置.将哨兵插入到该位置之后.

         

     

    9.2.4 代码实现

/*直接插入排序(从小到大),0号单元作为暂存单元和监视哨*/
void InsertSort(int r[],int n)
{
    int i,j;
    /*重复下述过程,直到所有元素都排序完毕。
    * 从下标为2的位置开始,下标为1的位置可以认为是已经排好序*/
    for(i=2;i<=n;i++)
    {
        /*暂存新元素,设置哨兵*/
        r[0] = r[i];
        /*新元素与排好序的序列从后往前进行比较,
        * 若新元素小于该元素,则循环,并将该元素后移一位*/
        for(j=i-1;r[j]>r[0];j--)
        {
            r[j+1] = r[j];
        }
        /*新元素大于或等于该元素时,则将新元素插入到该元素后*/
        r[j+1] = r[0];
    }
}


   9.2.5

   稳定性: 稳定的排序方式.

   时间复杂度: O(n^2) .


   9.3 希尔排序

      9.3.1 基本思想:

               先将整个待排序记录序列分割成若干个子序列,在子序列内分别进行直接插入排序,

        待整个序列基本有序时,再对全体记录进行一次直接插入排序.

      9.3.2  具体排序过程:

                假设待排序的记录为n个,先取整数d<n,例如,取d = n/2下取整,将所有相距为d的记录构成一组,从而将整个

         待排序记录序列分割成为d个子序列.

                对每个子序列分别进行直接插入排序,然后再缩小间隔d.例如,取d = d/2下取整,重复上述分割,再对每个子

         序列分别进行直接插入排序,直到最后取d = 1,即将所有记录放在一组进行一次直接插入排序,最终将所有记录

         重新排列成按关键码有序的序列.

     9.3.3 伪代码

               1. 取d = n/2作为增量,之后以d = d/2作为增量,把待排序列分成d个组.

               2. 将所有距离为d的倍数的记录放在一个子序列中.

               3. 将新记录与前d个位置的记录比较,若新纪录小于已排序子序列的记录

                   3.1 记录后移d个位置.

              4.  当新纪录大于或等于已排序子序列的记录,将新纪录插入该位置.

    

      9.3.4 代码实现

void ShellSort(int r[],int n)
{
    int d,i,j;
    /*以增量为d进行直接插入排序*/
    for(d = n/2;d >= 1;d = d/2)
    {
        /*从子序列的第二个元素开始*/
        for(i = d+1;i <= n;i++)
        {
            r[0] = r[i];   /*暂存待插记录*/
            for(j=i-d;j>0&&r[j]>r[0];j=j-d)
            {
                r[j+d] = r[j]; /*记录后移d个位置*/
            }
            r[j+d] = r[0];
        }
    }
}


    9.3.5

         稳定性:不稳定排序算法

         平均时间复杂度: 在O(nLog2ⁿ) ~O(n^2),在某个特定范围时,希尔排序的时间性能约为O(n^1.3) .


 交换排序

  9.4  冒泡排序

      9.4.1 基本思想

              两两比较相邻记录的关键码,如果反序则交换,直到没有反序的记录为止. 

      9.4.2 具体排序过程

              ① 将整个待排序的记录序列划分成有序区和无序区,初始时有序区为空,无序区包含所有待排序的记录.

              ②  对无序区从前向后依次将相邻记录的关键码进行比较,若反序则交换,从而使得关键码小的记录向前移,关键码大的向后移.

              ③ 重复执行②,直到无序区中没有反序的记录.

     9.4.3 伪代码

              1. 控制冒泡排序的趟数从1~n

                  1.1 执行一趟冒泡排序

                        1.1.1 如果当前值比后面的值大,则交换.

                  


    9.4.4 代码实现

/*从小到大排序*/

void BubbleSort(int r[],int n)
{
    int i,j;
    /*0号单元用作交换操作的暂存单元*/
    /*控制冒泡排序的趟数从1~n*/
    for(i=1;i<n;i++)
    {
        /*执行一趟排序*/
        for(j=2;j<n;j++)
        {
            /*前面的值比后面的大时,交换*/
            if(r[j-1]>r[j])
            {
                r[0]=r[j-1];
                r[j-1]=r[j];
                r[j]=r[0];
            }
        }
    }
}


/*改进算法*/
void BubbleSort1(int r[],int n)
{
    int i,j;
    bool flag = true;
    /*0号单元用作交换操作的暂存单元*/
    for(i=1;i<n && flag==true;i++)
    {
        flag = false;
        for(j=2;j<n;j++)
        {
            if(r[j-1]>r[j])
            {
                r[0]=r[j-1];
                r[j-1]=r[j];
                r[j]=r[0];
                flag = true;
            }
        }
    }
}

/*改进算法*/
void BubbleSort2(int r[],int n)
{
    int j;
    int exchange=n;
    int bound;
    /*0号单元用作交换操作的暂存单元*/
    while(exchange != 0)
    {
        bound = exchange;
        exchange = 0 ;
        for(j=1;j<bound;j++)
        {
            if(r[j]>r[j+1])
            {
                r[0]=r[j];
                r[j]=r[j+1];
                r[j+1]=r[0];
                exchange = j;
            }
        }
    }
}

 9.4.5

    平均时间复杂度: O(n^2)

    稳定性: 稳定的排序方法  

 

9.5 快速排序

    9.5.1 基本思想

            首先选取一个轴值(povit,即比较的基准),将待排序记录分成独立的两部分,左侧记录的关键码均小于或等于轴值,

      右侧的关键码均大于或等于轴值,然后分别对左右两部分重复执行上述过程,直到整个序列有序.

    9.5.2  具体排序过程

            ① 从序列中选取一个记录作为轴值.

            ② 将比轴值大的数,放到轴值右边;将比轴值小的数,放到轴值的左边.

            ③ 再对左右分区重复步骤②,直到各分区只有一个数(即各分区的轴值).


      9.5.3  伪代码(一次划分)

          1. 将i和j分别指向待划分区间的最左侧记录和最右侧记录的位置.

          2. 重复下述过程,直到 i = j

               2.1   右侧扫描,直到记录 j 的关键码小于轴值记录的关键码

               2.2   如果存在划分区间,则将 r[j]  与 r[i]  交换,并执行 i++

               2.3   左侧扫描,直到记录  i 的关键码大于轴值记录的关键码

               2.4   如果存在划分区间, 则将  r[i]  与 r[j] 交换,并执行 j--

         3.  退出循环,说明 i 和 j指向了轴值记录的所在位置,返回该位置


    9.5.4 代码实现

    

/*一次划分,0号单元作为交换操作的辅助单元*/
int Partition(int r[],int first,int end)
{
    /*将i和j分别指向待划分区间的最左侧记录和最右侧记录的位置*/
    int i = first,j = end;
    /*重复执行下述操作,直到i=j*/
    while(i<j)
    {
        /*右侧扫描,直到记录j的关键码小于轴值*/
        while(i<j && r[i]<=r[j])
        {
            j--;
        }
        /*若存在划分区间,则将r[j] 与 r[i]交换,并执行 i++*/
        if(i<j)
        {
            r[0] = r[i];
            r[i] = r[j];
            r[j] = r[0];
            i++;
        }
        /*左侧扫描,直到记录j的关键码大于轴值*/
        while(i<j && r[i]<=r[j])
        {
            i++;
        }
        /*若存在划分区间,则将r[i] 与 r[j]交换,并执行j--*/
        if(i<j)
        {
            r[0] = r[i];
            r[i] = r[j];
            r[j] = r[0];
            j--;
        }
   }
   /*推出循环,说明i和j指向了轴值所在位置,返回该位置*/
   return i;
}


void QuickSort(int r[],int first,int end)
{
    int povit;
    /*区间长度大于1,执行依次划分,否则递归结束*/
    if(first < end)
    {
        /*一次划分*/
        povit = Partition(r,first,end);
        /*递归对左测区间进行快速排序*/
        QuickSort(r,first,povit-1);
        /*递归对右侧区间进行快速排序*/
        QuickSort(r,povit+1,end);
    }
}

//方法一:
/*快速排序非递归算法*/
void QuickSort1(int *data, int first, int end)
{
	if (data == NULL || first < 0 || end < 0)
	{
		return;
	}
	int pivot;
	int pp;
	stack<int> stack;
	int pEnd = end; //记录最后元素位置
	//cout << "stack:" << stack.empty() << endl;
	stack.push(end);
	while (first < end || !stack.empty())
	{
		while (first < end)
		{
			pivot = Partition(data, first, end);
			stack.push(pivot);
			end = pivot - 1;
		}
		if (!stack.empty())
		{
			pivot = stack.top();
		        stack.pop();
			//当划分到前半部分(即还没有用到最后值的时候),
			//此时first,end分别取栈顶两个元素之间的最小和最大值
			if (pivot != pEnd && stack.top() != pEnd)
			{
				first = pivot + 1;
				end = stack.top() - 1;
			}
			//当划分到后半部分包含end时,
			//此时让first=pivot+1, end = pEnd.目的是为了将最后元素也加入划分中
			else if (pivot != pEnd && stack.top() == pEnd)
			{
				first = pivot + 1;
				end = pEnd;
			}
			//当栈顶为end时,说明排序已经结束
			else if (pivot == pEnd)
			{
				first = end;
			}
		}
	}
}


//方法二:

void QuickSort2(int *data, int first, int end)
{
if (data == NULL || first < 0 || end < 0)
{
return;
}
int pivot;
stack<int> stack;
pivot = Partition(data, first, end);
if (first < pivot - 1)
{
stack.push(first);
stack.push(pivot - 1);
}
if (end > pivot + 1)
{
stack.push(pivot + 1);
stack.push(end);
}
while (!stack.empty())
{
int j = stack.top(); //获取结尾下标
stack.pop();
int i = stack.top(); //获取开始下标
stack.pop();
//进行划分
pivot = Partition(data, i, j);
if (i < pivot - 1)
{
stack.push(i);
stack.push(pivot - 1);
}
if (j > pivot + 1)
{
stack.push(pivot + 1);
stack.push(j);
}
}
}


   9.5.5

   平均时间复杂度: O(nLog2ⁿ)

    稳定性: 不稳定排序算法

    


选择排序

   9.6 简单选择排序

        9.6.1 基本思想

                 第i趟排序在待排序序列 r[i]~r[j](1 <= i<=n-1)中选取关键码最小的记录,

                 并和第i个记录交换作为有序序列的第i个记录.

       9.6.2 具体实现过程

                ① 将整个记录序列划分成为有序区和无序区,初始时有序区为空,无序区含有待排序的所有记录 .

                ② 在无序区中选取关键码最小的记录,将它与无序区中的第一个记录交换,使得有序区增加一条记录,无序区减少一条记录

                ③ 不断重复执行②,直到无序区只剩下一条记录为止.此时所有记录已经按关键码从小到大的顺序排列.

      9.6.3 伪代码

               1. 对n个记录进行n-1趟简单选择排序,从i =1开始

               2. index 记录最小值的下标位置

               3. 从i+1位置开始在无序区中选择最小值

                    3.1 若r[j] < r[index] ,则index =   j

               4. 最小记录选择完毕,若index !=  i,则交换i与index位置的记录


      9.6.4 代码实现

/*简单选择排序,0号单元用作交换操作的暂存单元*/
void SelectSort(int r[],int n)
{
    int i,j,index;
    /*对n个记录进行n-1趟简单选择排序(由于简单选择排序到第n-1次后,
    * 无序区中只剩下一个值,且是最大值,因此只需要进行n-1趟排序)*/
    for(i=1;i<n;i++)
    {
        /*index 记录最小值*/
        index = i;
        /*第i位置元素与后面i+1到n的元素进行比较*/
        for(j=i+1;j<=n;j++)
        {
            /*若存在更小的记录,则index记录其位置*/
            if(r[j] < r[index])
            {
                index = j;
            }
        }

       /*如果index不是i位置,则说明找到更小的值*/
        if(index != i)
        {
            /*交换*/
            r[0] = r[i];
            r[i] = r[index];
            r[index] = r[0];
        }
    }

}


     9.6.5

        时间复杂度: O(n^2)
        稳定性: 不稳定的排序方法


   9.7  堆排序

        9.7.1 堆的定义

               堆是具有下列性质的完全二叉树(完全二叉树是描述堆的工具):每个节点的值都小于或等于其左右孩子的值(称为小根堆);

               或者每个结点的值都大于或等于其左右孩子的值(大根堆).


       9.7.2 筛选算法伪代码

              1. 设置i和j,分别指向当前要筛选的结点和要筛选节点的左孩子;

              2. 若节点i已经是叶子,则筛选完毕,算法结束;否则,执行下述操作:

                  2.1 将 j 指向节点 i 的左右孩子中较大者;

                  2.2 如果 r[i] > r[j] ,  则筛选完毕,算法结束;

                  2.3 如果 r[i] < r[j] ,则将r[i]与r[j]交换; 令 i = j,转步骤2继续筛选;


      9.7.3 代码实现

/*堆筛选算法,0号单元用作交换操作的暂存单元*/
void Sift(int r[],int k,int m)
{
    /*i指向被筛选结点,j指向结点i的左孩子*/
    int i = k,j = 2*i;
    /*筛选还没进行到左孩子*/
    while(j <= m)
    {
        /*比较i的左右孩子,j指向较大者*/
        if(j<m && r[j]<r[j+1]) //(2i+1)<=m,则i的右孩子为2i+1
        {
            j++;
        }
        /*如果r[i]>r[j],则筛选完毕,算法结束*/
        if(r[i]>r[j])
        {
            break;
        }
        /*否则,即r[i]<r[j],则将r[i]与r[j]交换*/
        else{
            r[0] = r[i];
            r[i] = r[j];
            r[j] = r[0];
            i=j;    //被筛选结点位于原来结点j的位置
            j=2*i;
        }
    }
}


           


      9.7.4 堆排序基本思想(大根堆):

              ① 首先将待排序的记录序列构造成一个堆(大根堆),此时,选出堆中所有记录的最大者(即堆顶记录).

              ② 然后将堆顶记录移走,并将剩余的记录再调成堆,这样就找出了次大的记录.

              ③ 重复执行步骤②,直到堆中只有一个记录为止.

      9.7.5 伪代码

             1. 初始建堆,从最后一个分支节点到根结点(即从n/2到1位置的结点)

             2. 重复执行移走堆顶记录及重建堆的操作


     9.7.6 代码实现

 /*堆排序算法,0号单元用作交换操作的暂存单元*/
void HeapSort(int r[],int n)
{
    int i;
    /*初始建堆,从最后一个分支结点到根结点*/
    for(i = n/2;i>=1;i--)
    {
        Sift(r,i,n);
    }
    /*重复执行移去堆顶结点及重建堆的操作*/
    for(i = 1;i<n;i++)
    {
        /*将堆顶记录移动到数组后方有序区中*/
        r[0] = r[1];
        r[1] = r[n-i+1];
        r[n-i+1] = r[0];
        /*重建堆*/
        Sift(r,1,n-i);
    }
}

    9.7.7

        平均时间复杂度:O(nLog2ⁿ)

       稳定性: 不稳定的排序方法


   9.8 归并排序

         9.8.1 归并排序基本思想

                 将若干有序序列逐步归并,最终归并为一个有序序列

         9.8.2 二路归并排序基本思想

                 将若干有序序列进行两两归并,直至所有待排序记录都在一个有序序列位置.

         9.8.3 具体排序过程

                ① 将具有n个待排序的记录序列看成是n个长度为1的有序序列

                ② 进行两两归并,得到n/2(上取整) 个长度为2(最后一个有序序列的长度可能是1)的有序序列

                ③ 再进行两两归并,得到 n/4(上取整) 个长度为4(最后一个有序序列的长度可能小于4)的有序序列

                ④ 直至得到一个长度为n的有序序列

           9.8.4 代码实现

/*对两个有序序列r[s]~r[m]和r[m+1]~r[t]进行一次归并排序*/
void Merge(int r[],int r1[],int s,int m,int t)
{
    /*i指向第一个有序序列的第一个记录,
    * j指向第二个有序序列的第一个记录*/
    int i=s,j=m+1,k=s;
    /*当i<m并且j<t,执行下述操作*/
    while(i<=m && j<=t)
    {
        /*取r[i]和r[j]中较小者放入r1[k]中,
        * 并各自指向下一条记录*/
        if(r[i]<=r[j])
        {
            r1[k++]=r[i++];
        }else{
            r1[k++]=r[j++];
        }
    }
    /*若第一个子序列没有处理完,则进行收尾处理*/
    if(i<=m)
    {
        while(i<=m)
        {
            r1[k++] = r[i++];
        }
    }
    /*若第二个子序列没有处理完,则进行收尾处理*/
    else{
        while(j<=t)
        {
            r1[k++] = r[j++];
        }
    }
}


/*一趟归并排序,分为三种情况:
 * 1. 若i<= n-2h+1,则表示待归并的两个相邻有序序列的长度均为h,
 *    执行依一次归并,完成后i加2h,准备下一次归并。
 * 2. 若i< n-h+1,则表明仍有两个现邻有序序列,一个长度为h,另一个长度小于h,
 *    执行这两个有序序列的归并,完成后退出一趟归并。
 * 3. 若i>= n-h+1,则表明只剩下一个有序序列,直接将该序列送到r1的相应位置,
 *    完成后退出一趟归并*/
void MergePass(int r[],int r1[],int n,int h)
{
    int i=1;
    int k;
    /*情况1:待归并记录至少有两个长度为h的子序列*/
    while(i<= n-2*h+1)
    {
        Merge(r,r1,i,i+h-1,i+2*h-1);
        i=i+2*h;
    }
    /*情况2:待归并记录中还有两个子序列,且有一个长度小于h*/
    if(i<n-h+1)
    {
        Merge(r,r1,i,i+h-1,n);
    }
    /*情况3:待归并记录中只剩下一个子序列*/
    else{
        for(k=i;k<=n;k++)
        {
            r1[k] = r[k];
        }
    }
}

/*归并排序非递归排序,从下标1开始存放待排序序列。
 * 开始时,有序序列的长度为1,结束时,有序序列的长度为n。
 * 因此,可以用有序序列的长度来控制排序过程的结束。
 * 另外,如果排序趟数为奇数,则最终结果在辅助数组r1中,应传回到数组r中*/
void MergeSort1(int r[],int r1[],int n)
{
    /*初始时,子序列长度为1*/
    int h=1;
    while(h<n)
    {
        /*待排序序列从数组r中传到r1中*/
        MergePass(r,r1,n,h);
        h = 2*h;
        /*待排序序列从数组r1中传到r中*/
        MergePass(r1,r,n,h);
        h = 2*h;
    }
}

/*归并排序递归算法*/
void MergeSort2(int r[],int r1[],int s,int t)
{
    int m;
    /*待排序序列只有一个记录,递归结束*/
    if(s == t)
    {
        r1[s] = r[s];
    }else{
        m = (s+t)/2;
        MergeSort2(r,r1,s,m);  /*归并排序前半个子序列*/
        MergeSort2(r,r1,m+1,t); /*归并排序后半个子序列*/
        Merge(r1,r,s,m,t);    /*将已排好序的子序列归并*/
    }
}

  

   9.8.5

     平均时间复杂度: O(nLog2ⁿ)

     稳定性:稳定的排序算法



 分配排序

      9.9  桶式排序

           9.9.1 基本思想

                  假设待排序记录的值都在 0 ~ m-1之间,设置m个桶, 首先将值为i的记录分配到第i个桶中,

             然后再将各个桶中的记录依次收集起来.

          9.9.2 具体实现过程

                  ①分配操作:将记录插入到相应的队列中,入队在静态链表上实现,并修改相应的队头指针和队尾指针

                  ②收集操作:将所有队列首尾相接

         9.9.3 代码实现

/*分配排序*/
/*分配算法,first为静态链表的头指针,从下标0开始存放待排序序列*/
void Distribute(Node r[],int n,QueueNode q[],int m,int first)
{
    int k;
    int i = first;
    /*依次分配每一个待排序记录*/
    while(r[i].next != -1)
    {
        k=r[i].key;
        /*如果队列为空,则直接将下标i放入到对应的队列中*/
        if(q[k].front == -1)
        {
            q[k].front = i;
        }
        /*如果队列不为空,则在静态链表中实现插在队列尾部*/
        else{
            r[q[k].rear].next = i;
        }
        q[k].rear = i; //修改队尾指针
        i=r[i].next;   //i后移,处理下一个记录
        }
        q[k].rear = i; //修改队尾指针
        i=r[i].next;   //i后移,处理下一个记录
    }
}


/*收集算法,将所有队列首尾相接*/
int Collect(Node r[],int n,QueueNode q[],int m,int first)
{
    int last; //用于记录静态链表的尾标志
    int k = 0;
    /*找到第一个非空队列*/
    while(q[k].front != -1)
    {
        k++;
    }
    first = q[k].front; //first为第一个记录
    //printf("fffffff:%d\n",first);
    last = q[k].rear;   //设置last为队列k的最后一个记录
    while(k<m)
    {
        k++;
        if(q[k].front != -1) //第k个对了非空
        {
            /*将队列k的队头和前一个队列的队尾相接*/
            r[last].next = q[k].front;
            /*last为当前收集后的最后一个记录*/
            last = q[k].rear;
        }
    }
    r[last].next = -1; //在静态链表中设置尾标志
    return first;
}

void BucketSort(Node r[],int n,int m)
{
    int i,first;
    QueueNode q[m];
    /*初始化静态链表*/
    for(i=0;i<n;i++)
    {
        r[i].next = i+1;
    }
    /*设置尾标志和头指针*/
    r[n-1].next = -1;
    first = 0;
    /*初始化m个静态队列的队头和队尾指针*/
    for(i=0;i<m;i++)
    {
        q[i].front = q[i].rear = -1;
    }
    //进行分配
    Distribute(r,n,q,m,first);
    //进行收集,first指向的静态链表有序
    first = Collect(r,n,q,m,first);
    //打印输出
    Print2(r,n,first);
}

void Print2(Node r[],int n,int first)
{

    while(r[first].next != -1)
    {
        printf("%d\n",r[first].key);
        first = r[first].next;
    }
}

   9.9.4
       平均时间复杂度:O(n+m)

       稳定性:稳定的排序算法



   9.10  基数排序

               多关键码排序的两种基本情况:

               ①最主位优先MSD(most significant digit first)

                     先按最主位关键码k0进行排序,然后将序列分割成若干子序列,每个子序列中的记录具有相同的k0值,

                 在分别对每个子序列按关键码k1进行排序,然后将子序列分割成若干个更小的子序列,每个更小的子序列

                 中的记录具有相同的k1值.以此类推,直至按最次位关键码kd排序,最后将所有子序列收集在一起得到一个有

                 序序列.

               ②最次位优先LSD(least  significant digit first)

                     从最次位关键码kd起进行排序,然后再按关键码次kd-1排序,依次重复,直至对最主关键码k0进行排序,

                 得到一个有序序列. LSD方法无须分割序列但要求按关键码kd-1 ~ k进行排序时采用稳定的排序方法.

          9.10.1 基本思想

                   将关键码看成由若干个子关键码复合而成,然后借助分配和收集操作采用LSD方法进行排序

          9.10.2 具体过程
                  
 ① 第一趟排序按最次位关键码kd-1将具有相同码值的记录分配到一个队列中,然后再一次收集起来,

                   得到一个按关键码kd-1有序的序列

                   ② 一般情况,第i趟排序按关键码kd-i将具有相同码值的记录分配到一个队列中,然后在依次收集起来,

                   得到一个按关键码kd-i有序的序列

       

       9.10.3 代码实现

         

/*分配排序*/
/*分配算法,first为静态链表的头指针,从下标0开始存放待排序序列*/
void Distribute(Node r[],int n,QueueNode q[],int m,int first,int j)
{
    int k;
    int i = first;
    /*依次分配每一个待排序记录*/
    while(r[i].next != -1)
    {
        k=r[i].key[j];
        /*如果队列为空,则直接将下标i放入到对应的队列中*/
        if(q[k].front == -1)
        {
            q[k].front = i;
        }
        /*如果队列不为空,则在静态链表中实现插在队列尾部*/
        else{
            r[q[k].rear].next = i;
        }
        q[k].rear = i; //修改队尾指针
        i=r[i].next;   //i后移,处理下一个记录
        }
        q[k].rear = i; //修改队尾指针
        i=r[i].next;   //i后移,处理下一个记录
    }
}


/*收集算法,将所有队列首尾相接*/
int Collect(Node r[],int n,QueueNode q[],int m,int first)
{
    int last; //用于记录静态链表的尾标志
    int k = 0;
    /*找到第一个非空队列*/
    while(q[k].front != -1)
    {
        k++;
    }
    first = q[k].front; //first为第一个记录
    //printf("fffffff:%d\n",first);
    last = q[k].rear;   //设置last为队列k的最后一个记录
    while(k<m)
    {
        k++;
        if(q[k].front != -1) //第k个对了非空
        {
            /*将队列k的队头和前一个队列的队尾相接*/
            r[last].next = q[k].front;
            /*last为当前收集后的最后一个记录*/
            last = q[k].rear;
        }
    }
    r[last].next = -1; //在静态链表中设置尾标志
    return first;
}

void BucketSort(Node r[],int n,int m,int d)
{
    int i,j,first;
    QueueNode q[m];
    /*初始化静态链表*/
    for(i=0;i<n;i++)
    {
        r[i].next = i+1;
    }
    /*设置尾标志和头指针*/
    r[n-1].next = -1;
    first = 0;
    /*初始化m个静态队列的队头和队尾指针*/
    for(i=0;i<m;i++)
    {
        q[i].front = q[i].rear = -1;
    }

    for(j=0;j<d;j++)

    {

    //进行分配
    Distribute(r,n,q,m,first);
    //进行收集,first指向的静态链表有序
    first = Collect(r,n,q,m,first);

    }  

   //打印输出
    Print2(r,n,first);
}

void Print2(Node r[],int n,int first)
{

    while(r[first].next != -1)
    {
        printf("%d\n",r[first].key);
        first = r[first].next;
    }
}


9.10.4

       平均时间复杂度:O(d(n+m))

       稳定性:稳定的排序算法

               

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值