排序算法归类和实现

1.排序算法的分类

image.png

内排序和外排序概念
内排序:指在排序期间数据对象全部存放在内存的排序。
外排序:指在排序期间全部对象太多,不能同时存放在内存中,必须根据排序过程的要求,不断在内,外存间移动的排序。

2.排序算法思想和实现

我们平时面试时遇到的大多数是内排序,现在总结一个内排序各个方法的思想和实现。

(1)直接插入排序法

算法思想:每趟 将一个待排序的元素作为关键字,按照其关键字的大小插入到已经排好的部分序列的适当位置上,直到插入完成。

void InsertSort(int R[],int n){ //待排数据存在R[]中,默认为整型,个数n
        int i,j;
        int temp;
        for(i=2;i<=n;++i){ //数组从下标1 开始存储,第一个元素有序,所以从第二个开始处理
            temp=R[i];  //将待插入元素暂存于temp中
            j=i-1;

            /*下面这个循环完成了从待排元素之前的元素开始扫描,如果大于待排元素,则后移一位*/
            while(j>=1&&temp<R[j]){
                R[j+1]=R[j];
                --j;
            }
            R[j+1]=temp; //找到插入位置,将temp中暂存的待排元素插入
        }

    }

(2)二分法插入排序(也叫折半插入排序)

算法思想:基本思想和直接插入排序一样,区别在于寻找插入位置的方法不同;二分插入排序采用二分法查找插入位置。

        /*** 折半插入排序  */ 
        private static void binaryInsertSort ( int[] array,int n ) { 
           for ( int i = 1; i < n; i++ ){ 
                // if (array[i] > array[i - 1]) // 从大到小 
                if (array[i] < array[i - 1]) { // 从小到大  
                    int tmp = array[i]; 
                    int low = 0; 
                    int high = i - 1; 
                    while (low <= high) { 
                        int mid = ( low + high ) >>> 1;    // if (array[mid] > tmp) // 从大到小 
                        if (array[mid] < tmp){ // 从小到大 
                            low = mid + 1; 
                        } else  { 
                            high = mid - 1; 
                        } 
                    } 
                    for ( int j = i; j > low; j-- ){ 
                        array[j] = array[j - 1]; 
                    } 
                    array[low] = tmp; 
                } 
            } 
        } 

(3)希尔排序

希尔排序又称为缩小增量排序,也属于插入排序类的算法,是对直接插入排序的一种改进。
算法思想:将需要排序的序列划分为若干个较小的序列,对这些序列进行直接插入排序,通过这样的操作可使用需要排序的数列基本有序,最后再使用一次直接插入排序。

void shellInsert (list R,int n, int h){   //一趟插入排序,h为本趟增量
                int i, j, k;
                for (i = 1; i <= h; i++) {  //i为组号
                    for (j = h; j <= n; j += h) {  //每组从第二个记录开始插入
                        if (R[j].key >= R[j - h].key) continue;  //R[j]大于有序区的最后一个记录,不需要插入
                        R[0] = R[j];
                        k = j - h;
                        do {
                            R[k + h] = R[k];
                            k = k - h;   //后移记录继续向前搜索
                        } while ( k > 0 && R[0].key < R[k].key;);
                        R[k + h] = R[0];
                    }
                }
            }

(4)直接选择排序

算法思想:对n个记录进行扫描,选择最小的记录,将其输出,接着在剩下的n-1个记录中扫描,选择最小的记录将其输出,……不断重复这个过程,直到只剩一个记录为止。

void selectSort(int[] array,int n) {
        int min;
        int tmp = 0;
        for (int i = 0; i < n; i++) {
            min = array[i];
            for (int j = i; j < n; j++) {
                if (array[j] < min) {
                    min = array[j];//最小值
                    tmp = array[i];
                    array[i] = min;
                    array[j] = tmp;
                }
            }
        }
    }

(5)堆排序

堆是一个完全二叉树,树中每个结点对应于原始数据的一个记录,并且每个结点应满足以下条件:非叶结点的数据 大于或等于其左、右孩子结点的数据(若是按从大到小的顺序排序,则要求非叶结点的数据小于或等于其左、右孩子结点的数据)

由堆的定义可看出,其根结点为最大值,堆排序就是利用这一特点进行的。堆排序过程包括两个阶段:
①将无序的数据构成堆(即用无序数据生成满足堆定义的完全二叉树)。
②利用堆排序(即用上一步生成的堆输出有序的数据)。

void swap(int* a, int* b) {
    int temp = *b;
    *b = *a;
    *a = temp;
}
 
void max_heapify(int arr[], int start, int end) {
    //建立父节点指标和子节点指标
    int dad = start;
    int son = dad * 2 + 1;
    while (son <= end) { //若子节点指标在范围内才做比较
        if (son + 1 <= end && arr[son] < arr[son + 1]) //先比较两个子节点大小,选择最大的
            son++;
        if (arr[dad] > arr[son]) //如果父节点大於子节点代表调整完毕,直接跳出函数
            return;
        else { //否则交换父子内容再继续子节点和孙节点比较
            swap(&arr[dad], &arr[son]);
            dad = son;
            son = dad * 2 + 1;
        }
    }
}
 
void heap_sort(int arr[], int len) {
    int i; //初始化,i从最後一个父节点开始调整
    for (i = len / 2 - 1; i >= 0; i--)  //建堆 : len/2-1是最后一个非叶子节点位置
        max_heapify(arr, i, len - 1);
    //先将第一个元素和已排好元素前一位做交换,再重新调整,直到排序完毕
    for (i = len - 1; i > 0; i--) {
        swap(&arr[0], &arr[i]);
        max_heapify(arr, 0, i - 1);
    }
}
 
int main() {
    int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };
    int len = (int) sizeof(arr) / sizeof(*arr);
    heap_sort(arr, len);
    int i;
    for (i = 0; i < len; i++)
        printf("%d ", arr[i]);
    printf("\n");
    return 0;
}

(6)冒泡排序

算法思想:
第一个记录和第二个记录比,如果第一个大,则二者交换,否则不交换;然后第二个记录和第三个记录比较,如果第二个大,则交换,否则不交换。。。一直这样进行下去(冒泡排序算法的结束条件是在一趟排序过程中没有发生元素交换)。

void BubblSort(int[], int n){
        int i,j,flag; int temp;
        for (i = n; i >= 2; i--){  //数组从下标1开始存储
            flag = 0;
            for (j = 2; j <= i; ++j){
                if (R[j - 1] > R[j]) {
                    temp = R[j];
                    R[j] = R[j - 1];
                    R[j - 1] = temp; //交换
                    flag = 1;//如果发生交换,flag为1,没有发生交换,值为0
                }
        
                if (flag == 0) {
                    return;
                }
            }
        }
}

(7)快速排序

算法思想:
快速排序使用分治策略来把待排序数据序列分为两个子序列,具体步骤为:

  1. 在数据集之中,选择一个元素作为”基准”。
  2. 所有小于”基准”的元素,都移到”基准”的左边;所有大于”基准”的元素,都移到”基准”的右边。分区操作结束后,基准元素所处的位置就是最终排序后它的位置。
  3. 对”基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
void quickSort(int[] arr){
    qsort(arr, 0, arr.length-1);
}
void qsort(int[] arr, int low, int high){
    if (low < high){
        int pivot=partition(arr, low, high);        //将数组分为两部分
        qsort(arr, low, pivot-1);                   //递归排序左子数组
        qsort(arr, pivot+1, high);                  //递归排序右子数组
    }
}
 int partition(int[] arr, int low, int high){
    int pivot = arr[low];     //枢轴记录
    while (low<high){
        while (low<high && arr[high]>=pivot) --high;
        arr[low]=arr[high];             //交换比枢轴小的记录到左端
        while (low<high && arr[low]<=pivot) ++low;
        arr[high] = arr[low];           //交换比枢轴小的记录到右端
    }
    //扫描完成,枢轴到位
    arr[low] = pivot;
    //返回的是枢轴的位置
    return low;
}

(8)归并排序

算法思想:把待排序的文件分成若干个子文件,先将每个子文件内的记录排序,再将已排序的子文件合并,逐个得到完整的排序文件。

    public void mergeSort(int[] a,int left,int right,int n){ //n为数组长度
        if(left<right){
            int middle = (left+right)/2;
            mergeSort(a, left, middle);
            mergeSort(a,middle+1,right);
            merge(a,left,middle,right,n);//合并
        }
    }

    void merge(int[] a, int left, int middle, int right,int n) {
        int [] tmpArray = new int[n];
        int rightStart = middle+1;
        int tmp = left;
        int third = left;
        //比较两个小数组相应下标位置的数组大小,小的先放进新数组
        while(left<=middle&&rightStart<=right){
            if(a[left]<=a[rightStart]){
                tmpArray[third++] = a [left++];
            }else{
                tmpArray[third++] = a[rightStart++];
            }
        }
        //如果左边还有数据需要拷贝,把左边数组剩下的拷贝到新数组
        while(left<=middle){
            tmpArray[third++] = a[left++];
        }
        //如果右边还有数据......
        while(rightStart<=right){
            tmpArray[third++] = a[rightStart++];
        }
        while(tmp<=right){
            a[tmp] = tmpArray[tmp++];
        }
    }

(9)桶排序

将数组分到有限数量的桶里,每个桶再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序),最后依次把各个桶中的记录列出来记得到有序序列。

 void bucketSort ( int[] array, int min, int max,int n) 
        { 
            int[] tmp = new int[n]; 
            int[] buckets = new int[max - min]; 
            for ( int i = 0; i < n; i++ ) 
            { 
                buckets[array[i] - min]++; 
            } 
            // 从小到大 
            // for ( int i = 1; i < max - min; i++ ) 
            // { 
            // buckets[i] = buckets[i] + buckets[i - 1]; 
            // } 
            // 从大到小 
            for ( int i = max - min - 1; i > 0; i-- ) 
            { 
                buckets[i - 1] = buckets[i] + buckets[i - 1]; 
            } 
            System.arraycopy (array, 0, tmp, 0, n); 
            for ( int k = array.length - 1; k >= 0; k-- ) 
            { 
                array[--buckets[tmp[k] - min]] = tmp[k]; 
            } 
        } 

(10)基数排序(低位优先分配排序法)

基数排序:排序采用的主要数据结构是单链表表示的队列。
算法思想:把排序码分解成若干部分,然后通过对各个部分排序码的分别排序,最终实现对整个排序码的排序。
实现排序的方法:①高位优先法:从最高位排序码 k(0) 开始,逐个针对各个排序码排序。②地位优先法:从最低位排序码 k(d-1) 开始排序

struct Node{
        KeyType key[D];//排序码是数组
        DataType info;
        PNode next;
    }
    
    typedef struct{
        PNode f;//队列头指针
        PNode e;//队列尾指针
       }Queue; //用队列表示各个子序列
    typedef struct Node *RadixList; //待排序文件类型


void radixSort(RadixList *plist,int d,int r){
    int i,j,k; PNode p,head=(*plist)->next;
    final Queue queue[r];//队列数组
    for(j=d-1;j>=0;j--){//进行d次分配和收集
        for(i=0;i<r;i++){
            queue[i].f=queue[i].e=null;//清空队列
        }

        for(p=head;p!=null;p=p->next){
            k=p->key[j]; //按排序码的第j个分量分配
            if(queue[k].f==null){
                queue[k].f=p;//队列为空设队头指针
            }else {
                (queue[k].e)->next=p; //接到队列k尾
            }
            queue[k].e=p;
        }
        
        for(i=0;queue[i].f==null;i++);//找到第一个非空数列
         p=queue[i].e; head =queue[i].f; //head为收集链表的头指针  
        
        for(i++;i<r;i++){
            if (queue[i].f!=null){
                p->next=queue[i].f;
                p=queue[i].e;
            }
        }
        p->next=null;
    }
    
    (*plist)->next=head;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352