[C语言] 数据结构 严蔚敏版 块排+起泡是什么样子?书上与网络上搜集到的块排方法之终极改进版!

书中罗列的快速排序改进方法总结为:1 “三者取中”法则来选取枢轴记录 2 在指针high low减增的时候同时进行起泡操作,若没有进行过交换则代表不用排序。 值得注意的是,目前同类博客很少提到这种方法。实际上,这种方法不仅能减少交换次序,通过两个布尔值反应交换与否,还能够解决大量相同数字的块排问题。3 实现递归时判断两段数组的大小,选择小端优先递归,则栈的最大深度退化为Log(n),这也是块排最小的空间复杂度。

网络上块排的改进方法有:1 数组小于某个数的时候直接插入排序 2 采用“三次划分”的方法处理大量相同数字的块排问题。

综上,我写了两个块排的最终改进版。QSortMd 包含了书中所有的改进方法:三者取中,起泡操作,减小递归深度。

QSort5 包含了目前博客经常提到的所有改进方法:结合插入排序,三者取中,三次划分解决相同数字,减小递归深度。

时间复杂度不会算 性能改进效果有待测试,本文仅仅贴出改进的实现方法供大家借鉴

首先贴出来原版本的块排:

int Partition2 (SrtSqList *L, int low, int high)
{
    int pivotkey = L->r[low].key;
    L->r[0] = L->r[low];

    while (low<high){
        while (low<high && L->r[high].key>=pivotkey)
            high--;
        L->r[low] = L->r[high];
        while (low<high && L->r[low].key<=pivotkey)
            low++;
        L->r[high] = L->r[low];
    }
    L->r[low] = L->r[0];
    return low;
}
//改用0号单元暂存数据
void QSort (SrtSqList *L, int low, int high)
{
    int pivotloc;

    if (low<high){
        pivotloc = Partition2(L,low,high);
        QSort(L,1,pivotloc-1);
        QSort(L,pivotloc+1,high);
    }
}
void QuickSort (SrtSqList *L)
{
    QSort(L,1,L->length);
}

书中的最终改进版QSortMd (三者取中,起泡操作,减小递归深度)


int Partition4 (SrtSqList *L, int low, int high,Boolean *lowfg,Boolean *highfg)
{
    int pivotkey = DealPivot (L,low,high);
    int k; //冒泡遍历

    L->r[0] = L->r[low];

    while (low<high){
        while (low<high && L->r[high].key>=pivotkey){
            //冒泡比较high high+1
            for (k=high;k<=L->length-1 && L->r[k].key>L->r[k+1].key;k++){
                Swap(L,k,k+1);
                *highfg = TRUE;
            }
            high--;
        }
        L->r[low] = L->r[high];
        while (low<high && L->r[low].key<=pivotkey){
            for (k=low;k>=2 && L->r[k].key<L->r[k-1].key;k--){
                Swap(L,k,k-1);
                *lowfg = TRUE;
            }
            low++;
        }
        L->r[high] = L->r[low];
    }
    L->r[low] = L->r[0];
    return low;
}
//改用0号单元暂存数据并按“三者取中”法选择枢轴记录,加上起泡操作改进


int DealPivot (SrtSqList *L, int low, int high)
{
    int mid = (low+high)/2;
    if (L->r[low].key>L->r[mid].key)
        Swap(L,low,mid);
    if (L->r[low].key>L->r[high].key)
        Swap(L,low,high);
    if (L->r[mid].key>L->r[high].key)
        Swap(L,mid,high);

    return L->r[low].key;
}

void Swap (SrtSqList *L,int a, int b)
{
    RcdType tmp;
    tmp = L->r[a];
    L->r[a] = L->r[b];
    L->r[b] = tmp;
}

void QSortMd (SrtSqList *L, int low, int high)
{
    int pivotloc;
    Boolean lowfg;
    Boolean highfg;

    if (low<high){

        lowfg = FALSE;
        highfg = FALSE;
        pivotloc = Partition4(L,low,high,&lowfg,&highfg);
        if (L->length-pivotloc>pivotloc-1){
            if (lowfg==TRUE){
                QSortMd(L,1,pivotloc-1);
            }
            if (highfg==TRUE){
                QSortMd(L,pivotloc+1,high);
            }
        } else {
            if (highfg==TRUE){
                QSortMd(L,pivotloc+1,high);
            }
            if (lowfg==TRUE){
                QSortMd(L,1,pivotloc-1);
            }
        }
    }

} //改进栈的顺序,使深度不超log(n)
void QuickSort (SrtSqList *L)
{
    QSortMd(L,1,L->length);
}

网络上改进版(结合插入排序,三者取中,三次划分解决相同数字,减小递归深度)

int Partition5 (SrtSqList *L, int low, int high,int *leftlen,int *rightlen)
{
    int left = low;
    int right = high;
    *leftlen = *rightlen = 0; //记录相同元素

    int first = low;
    int last = high; //记录排序数组起始序号,放回相同元素时使用

    int pivotkey = DealPivot (L,low,high);

    L->r[0] = L->r[low];

    while (low<high){
        while (low<high && L->r[high].key>=pivotkey){
            //处理相等元素
            if (L->r[high].key==pivotkey){
                Swap(L,right,high);
                right--;
                *rightlen++;
            }
            high--;
        }
        L->r[low] = L->r[high];
        while (low<high && L->r[low].key<=pivotkey){
            if (L->r[low].key==pivotkey){
                Swap(L,left,low);
                left++;
                *leftlen++;
            }
            low++;
        }
        L->r[high] = L->r[low];
    }
    L->r[low] = L->r[0];

    //一次块排结束把与枢轴相同的元素移动到枢轴最终位置(即high==low)的周围
    int i = low - 1;
    int j = first;
    while (j<left && L->r[i].key!=pivotkey){
        Swap(L,i,j);
        i--;
        j++;
    }
    i = low + 1;
    j = last;
    while (j>right && L->r[i].key!=pivotkey){
        Swap(L,i,j);
        i++;
        j--;
    }

    return low;
}
//改用0号单元暂存数据并按“三者取中”法选择枢轴记录,元素较少时选择插入排序,重复元素特殊规划

int DealPivot (SrtSqList *L, int low, int high)
{
    int mid = (low+high)/2;
    if (L->r[low].key>L->r[mid].key)
        Swap(L,low,mid);
    if (L->r[low].key>L->r[high].key)
        Swap(L,low,high);
    if (L->r[mid].key>L->r[high].key)
        Swap(L,mid,high);

    return L->r[low].key;
}

void Swap (SrtSqList *L,int a, int b)
{
    RcdType tmp;
    tmp = L->r[a];
    L->r[a] = L->r[b];
    L->r[b] = tmp;
}

void QSort5 (SrtSqList *L, int low, int high)
{
    int pivotloc;

    int leftlen = 0;
    int rightlen = 0; //辅助处理相同元素

    if (low<high){
//        pivotloc = Partition(L,low,high);
        //还可以插排
        if (high-low+1<10){
            InsertSort(L,low,high);
            return;
        }
        pivotloc = Partition5(L,low,high,&leftlen,&rightlen);
        if (L->length-pivotloc>pivotloc-1){
            QSort5(L,1,pivotloc-1-leftlen);
            QSort5(L,pivotloc+1+rightlen,high);
        } else {
            QSort5(L,pivotloc+1+rightlen,high);
            QSort5(L,1,pivotloc-1-leftlen);
        }
    }
} //采取插排和处理同元素的做法

void QuickSort2 (SrtSqList *L)
{
    QSort5(L,1,L->length);
}

void InsertSort (SrtSqList *L,int low,int high) //插排
{
    int i,j;
    for (i=low+1;i<=high;i++){
        for (j=i;j>=low+1 && L->r[j].key<L->r[j-1].key;j--){
            Swap(L,j,j-1);
        }
    }
}

其他辅助文档:

SequenceList.h
#ifndef SEQUENCELISTTYPE_H
#define SEQUENCELISTTYPE_H

#include <stdio.h>
#include "Status.h"
#include "Scanf.h"

//内部排序的顺序表存储结构
//宏定义
#ifndef MAXSIZE
#define MAXSIZE 20
#endif // MAXSIZE

#define LT(a,b) ((a)<(b))
#define LQ(a,b) ((a)<=(b))

//类型定义
typedef int KeyType;
typedef struct
{
    KeyType key; //关键字项
    //在使用中拓展的结构体
}RcdType; //记录类型
typedef struct
{
    RcdType r[MAXSIZE+1]; //r[0]闲置
    int length; //顺序表长度
} SrtSqList;

//函数列表
Status CreateSortList (FILE *fp, SrtSqList *L);

void Traverse (SrtSqList L, void(*Visit)(KeyType));

#endif // SEQUENCELISTTYPE_H
SequenceList.c
#ifndef SEQUENCELISTTYPE_C
#define SEQUENCELISTTYPE_C

#include "SequenceListType.h"

//函数列表
Status CreateSortList (FILE *fp, SrtSqList *L)
{
    int i;

    Scanf(fp,"%d",&(L->length));

    if (L->length>MAXSIZE)
        return ERROR;
    else {
        for (i=1;i<=L->length;i++)
            Scanf(fp,"%d",&(L->r[i].key));
        return OK;
    }
}

void Traverse (SrtSqList L, void(*Visit)(KeyType))
{
    int i;

    for (i=1;i<=L.length;i++)
        Visit(L.r[i].key);

    printf("\n");
}

#endif // SEQUENCELISTTYPE_C
#ifndef SCANF_H
#define SCANF_H

#include <stdio.h>
#include <string.h>
#include <stdarg.h>				//提供宏va_list、va_start、va_arg、va_end
#include <ctype.h> 				//提供isprint原型

/*
    自定义的数据录入函数,用于从文件fp
中读取格式化的输入。

    与fscanf不同之处在于此函数只会读取
西文字符,对于中文字符,则会跳过。
*/
int Scanf(FILE *fp, char *format, ...);

#endif // SCANF_H
/*********************
 *                   *
 * 文件夹: ▲01 绪论 *
 *                   *
 * 文件名: Scanf.c   *
 *                   *
 *********************/

#ifndef SCANF_C
#define SCANF_C

#include "Scanf.h"

/*
    自定义的数据录入函数,用于从文件fp
中读取格式化的输入。

    与fscanf不同之处在于此函数只会读取
西文字符,对于中文字符,则会跳过。
*/

int Scanf(FILE *fp, char *format, ...)
{
	int *i;
	char *ch, *s;
	float *f;
	int count, k, len, n;
	int tmp;
	va_list ap;

	len = strlen(format);

	va_start(ap, format);

	for(count=0,k=2; k<=len; k=k+2)
	{
		while((tmp=getc(fp))!=EOF)			//跳过所有非西文字符
		{
			if((tmp>=0 && tmp<=127))
			{
				ungetc(tmp, fp);			//遇到首个西文字符,将此西文字符放入输入流
				break;
			}
		}

		if(tmp==EOF)
			break;

		if(format[k-1]=='c')				//读取字符
		{
			ch = va_arg(ap, char*);

			if(tmp!=EOF)
				count += fscanf(fp, "%c", ch);
		}

		if(format[k-1]=='d')				//读取整型
		{
			i = va_arg(ap, int*);

			while((tmp=getc(fp))!=EOF)
			{
				if((tmp>='0' && tmp<='9') || tmp=='-' || tmp=='+')
				{
					ungetc(tmp, fp);
					break;
				}
			}

			if(tmp!=EOF)
				count += fscanf(fp, "%d", i);
		}

		if(format[k-1]=='f')				//读取浮点型
		{
			f = va_arg(ap, float*);

			while((tmp=getc(fp))!=EOF)
			{
				if((tmp>='0' && tmp<='9') || tmp=='-' || tmp=='+'|| tmp=='.' )
				{
					ungetc(tmp, fp);
					break;
				}
			}

			if(tmp!=EOF)
				count += fscanf(fp, "%f", f);
		}

		if(format[k-1]=='s')				//读取字符串
		{
			s = va_arg(ap, char*);

			while((tmp=getc(fp))!=EOF && (!isprint(tmp) || tmp==' '))
				;

			n = 0;
			if(!feof(fp))
			{
				ungetc(tmp, fp);
				while((tmp=getc(fp))!=EOF)
				{
					if(isprint(tmp) && tmp!=' ')
						s[n++] = tmp;
					else
						break;
				}
				ungetc(tmp, fp);
			}

			s[n] = '\0';

			count++;
		}
	}

	va_end(ap);

	return count;
}

#endif
//主函数文档 用来测试
#include <stdio.h>
#include <stdlib.h>

#include "SequenceListType.h"

void PrintKey (KeyType e)
{
    printf("%d ",e);
}

int main()
{
    SrtSqList L;
    FILE *fp = fopen("TestData.txt","r");
    if (!fp)
        exit(-1);

    CreateSortList(fp,&L);
    fclose(fp);

    printf("创建并输出一个任意序列\n");
    Traverse(L,PrintKey);
    printf("\n");

    InsertSort(&L,1,L.length);
    Traverse(L,PrintKey);

    printf("快速排序法排序\n");
    QuickSort(&L);
    Traverse(L,PrintKey);
    printf("\n");
    QuickSort2(&L);
    Traverse(L,PrintKey);
    printf("\n");


    return 0;
}
数组文件录入格式:
个数 数字
例如:
3 1 2 3
或
(4) 3 2 4 1

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值