zoukankan      html  css  js  c++  java
  • 基本排序方法:冒泡排序、简单选择排序、直接插入排序

     #返回上一级

    @Author: 张海拔

    @Update: 2014-01-11

    @Link: http://www.cnblogs.com/zhanghaiba/p/3514440.html

      1 /*
      2  *Author: ZhangHaiba
      3  *Date: 2014-1-10
      4  *File: basic_sort_algorithm.c
      5  *
      6  *a demo of basic sort algorithm including:
      7  *bubble sort
      8  *select sort
      9  *insert sort
     10  */
     11 
     12 #include <stdio.h>
     13 #define N 512
     14 
     15 //public
     16 void bubble_sort(int*, int);
     17 void select_sort(int*, int);
     18 void insert_sort(int*, int);
     19 void insert_sort2(int*, int);
     20 void set_array(int*, int);
     21 void show_array(int*, int);
     22 //private
     23 void swap(int*, int*);
     24 
     25 int array[N];
     26 
     27 int main(void)
     28 {
     29     int n;
     30     
     31     scanf("%d", &n);
     32     set_array(array, n);
     33     //basic sort algorithm
     34     insert_sort(array, n);
     35     show_array(array, n);
     36     return 0;
     37 }
     38 
     39 void bubble_sort(int *a, int n)
     40 {
     41     int i, j;
     42     
     43     for (i = 0; i < n-1; ++i)
     44         for (j = 0; j < n-1-i; ++j)
     45             if (a[j] > a[j+1])
     46                 swap(&a[j], &a[j+1]);
     47 }
     48 
     49 void select_sort(int *a, int n)
     50 {
     51     int i, j, min_idx;
     52 
     53     for (i = 0; i < n-1; ++i) {
     54         min_idx = i;
     55         for (j = i; j < n; ++j)
     56             if (a[j] < a[min_idx])
     57                 min_idx = j;
     58          swap(&a[i], &a[min_idx]);
     59     }
     60 }
     61 
     62 void insert_sort(int *a, int n)
     63 {
     64     int i, j, key;
     65 
     66     for (i = 1; i < n; ++i) {
     67         key = a[i];
     68         for (j = i-1; j >= 0 && a[j] > key; --j)
     69             a[j+1] = a[j];
     70         a[j+1] = key;
     71     }
     72 }
     73 
     74 //a simple and concise implementation of insert_sort
     75 void insert_sort2(int *a, int n)
     76 {
     77     int i, j;
     78     
     79     for (i = 1; i < n; ++i)
     80         for (j = i-1; j >= 0 && a[j] > a[j+1]; --j)
     81             swap(&a[j], &a[j+1]);
     82 }
     83 
     84 void set_array(int *a, int n)
     85 {
     86     int i;
     87     
     88     for (i = 0; i < n; ++i)
     89         scanf("%d", a+i);
     90 }
     91 
     92 void show_array(int *a, int n)
     93 {
     94     int i;
     95 
     96     for (i = 0; i < n; ++i)
     97         printf(i == n-1 ? "%d
    " : "%d ", a[i]);
     98 }
     99 
    100 void swap(int *a, int *b)
    101 {
    102     int tmp = *a;
    103     *a = *b;
    104     *b = tmp;
    105 }

    理解排序算法,需要掌握两个要点:

    (1)排序通常是使待排序数组规模从n减到1(或看做已排序数组规模从1增到n)的过程;//规模为1的数组必然有序

    (2)排序的问题规模减1这个过程,一般称为“一趟排序”;

    冒泡排序(bubble_sort):

    冒泡排序是最易理解和编写的一个排序方法,由于一趟排序的过程有如气泡出水底中冒上来那样生动,因而得名。

    冒泡排序通过依次让[0, n-1)的元素与它右边紧领(下称“右继”)的元素比较,使较大(或较小)的元素落在右边位置,

    这样,对当前待排序数组按上述过程一次遍历,就能使最大(或最小)元素落在最右端,也就一趟排序后,待排序数组规模减1了。

    规模从n减到1,需要n-1次,即通过n-1趟排序就能完成了整个排序。

    代码中——

    外循环for (i = 0; i < n-1; ++i)控制n-1趟排序,

    内循环一次确保最大元素落在待排序数组的最右端:for (j = 0; j < n-1 - i; ++j)  if (a[j] > a[j+1]) swap(j, j+1);

    简单选择排序(select_sort):

    设min_idx是规模为n的待排序数组中已知元素中最小元素的下标;

    为了维护min_idx值的意义,初始化为起始元素下标,(嗯,改名叫current_min_idx最适合)

    并且在遍历待排序数组的过程中,始终维护min_idx始终记录当前已知元素中最小元素的下标(指向当前最小元素);

    这样,循环遍历结束时,min_idx为当前待排序数组中最小元素的下标。

    此时交换当前数组最左元素与最小元素,即使未排序数组规模减1。

    代码中——

    显然外层循环控制n-1躺排序;

    内存循环求出待排序数组中最大元素的下标,再交换最大元素与最左元素,使排排序数组规模减1。

     

    直接插入排序(insert_sort):

    直接插入排序思路,与上述两种排序思路反过来想会更好理解——

    即通过不断增加已排序数组的规模,最终得到规模为n的已排序数组。

    最初,有序数组的规模是1(第一元素看做一个规模为1的已排序数组)。

    已排序数组增1(从k到k+1)的过程(即一趟排序)是:

    通过把 与已排序数组的右继元素 “有序地”插入到已排序数组(即插入到适当位置使数组仍有序,但规模必然加1)。

    那么对于规模为i的已排序数组,设j=i-1(指向已排序数组的最后一个元素),

    则从已排序数组的末尾元素a[j]开始,与其右继元素a[j+1]进行比较,若a[j+1]小则交换位置,

    这样逆序两两比较,直到出现!(a[j] > a[j+1]) || j < 0,此时说明插入的元素已满足有序性,不再需要交换。

    (再回味下这个过程,其实是让当前规模有序数组的右继元素,通过几次交换“有序地”插入到上述数组)

    这样,已排序数组的规模加1。

    代码中——

    内循环for (j = i-1; j >= 0 && a[j] > a[j+1], ++j) swap(j, j+1); 其中i表示当前有序数组的规模;

    外循环控制有序数组规模,规模从1开始(变换为2),一直到n-1(变换为n),因此有:for (i = 1; i < n; ++i)。

     

    三个算法的比较:

    我的实现中,冒泡排序和直接插入排序都可以用三行代码写完,简单选择排序则长了点。

    而性能方面,虽然三者最坏情况都是O(n^2),但冒泡排序和直接插入排序最好情况是O(n),简单选择排序最好情况也是O(n^2)

    最关健的是,冒泡排序和直接插入排序显然是稳定的,而简单选择则是不稳定的。举个例子(2, 2, 1),一次排序后是(1, 2, 2),显然2的相对次序改变了。

    Hint:算法的稳定性是指:原始数据中相同的关键字的相对次序在排序后没有改变,则称这种排序算法是稳定的。

    综合来看:简单选择排序比较挫了(代码长度、最好情况、稳定性都劣势),冒泡排序居中,直接插入排序对于数据“基本有序”则表现很好。

    三个算法的改进:

    (1)快速排序的基于冒泡排序来改进的,不过这次冒的泡不是最轻的,而是期望这个泡重量夹在中间,这样可以通过分治法使效率提升到O(n*logn),但这种改进丢失冒泡稳定的特性。

    (2)堆排序是基于选择排序改进,首先堆是指二叉堆这个数据结构,这个数据结构可以以O(logn)的效率求出最值,从而使选择选择排序效率提升到O(n*logn),这种改进也丢失了简单选择稳定的特性。

    (3)希尔排序是基于直接插入排序的改进,改进的基本原理是:当数组“基本有序”时,直接插入效率接近O(n),然后通过隔空进行直接插入排序减小问题规使得相对效率提升,而随着隔空距离的减小,数据的“基本有序”的程序却在增大。通过数学证明,平均情况下希尔排序的效率提升到了O(n^(1.3))。

    最后,对排序算法来个分类——

    (1)交换类排序:冒泡排序、快速排序

    (2)选择类排序:简单选择排序、堆排序

    (3)插入类排序:直接插入排序、希尔排序

    (4)归并类排序:归并排序

    #返回上一级

  • 相关阅读:
    Netty指定分隔的字符
    Netty解决TCP粘包/拆包问题
    TCP粘包/拆包问题
    Netty5-应答服务器
    线程池中的线程的排序问题
    Java NIO 概述
    在windows中,如何使用cmd命令行窗口正确显示编码为utf-8格式的文字
    OXM
    Mysql event时间触发器,实现定时修改某些符合某一条件的某一字段
    linux 环境 tomcat 莫名奇妙挂掉
  • 原文地址:https://www.cnblogs.com/zhanghaiba/p/3514440.html
Copyright © 2011-2022 走看看