zoukankan      html  css  js  c++  java
  • 【算法与数据结构】三种简单排序

    冒泡排序

    逐个比较相邻元素,如果逆序则交换。每一趟都会让一个元素就位,总共比较 n-1 趟。

    n 个元素,需要进行 n - 1 轮冒泡,每次冒泡都会有一个元素就位,所以每轮冒泡的循环次数都会减一。时间复杂度为:
    1 + 2 + … + n-1 = n(n-1)/2 = O(n^2)

    初始值:		5 4 3 2 1 
    首轮冒泡:	4 3 2 1 5  最后一个元素就位
    第二轮:		3 2 1 4 5  倒数第二个元素就位
    ...
    

    基础版冒泡排序

    // 冒泡排序,每轮都会排好一个元素
    void bubbleSort(int arr[], int count) {
        int i, j;
        // n 个元素,需要比较 n-1 轮
        for (i = count - 1; i > 0; i--) {
            // 每轮比较,只需在剩余的 j 个元素中,从头开始比较 j-1 次
            for (j = 1; j <= i; j++) {
                if (arr[j-1] > arr[j]) {
                    swap(arr, j - 1, j);
                }
            }
        }
    }
    

    提前结束的冒泡排序

    上面的冒泡排序,即使剩余元素都已经有序,内层 for 循环中没有发生元素交换,也会继续执行下去。可以设置一个值,在剩余元素都已经就位时提前结束循环:

    void bubbleSort(int arr[], int count) {
        int i, j, sorted;
        // n 个元素,需要比较 n-1 轮
        for (i = count - 1; i > 0; i--) {
        	sorted = 1;
            // 每轮比较,只需在剩余的 j 个元素中,从头开始比较 j-1 次
            for (j = 1; j <= i; j++) {
                if (arr[j-1] > arr[j]) {
                    swap(arr, j - 1, j);
                    sorted = 0;
                }
            }
            if (sorted == 1) {
    			break;
    		}
        }
    }
    

    标记有序区间的冒泡排序

    对于 5 4 3 2 1 9 6 7 8 这样的数据,第一轮排序之后,最后的部分元素都会就位,而不是仅仅就位一个元素。如果每次都标记有序的区间,也可以改善冒泡排序的效率:

    void bubbleSort(int arr[], int count) {
    	int i, j;
    	int last = count - 1;
    	for (i = count - 1; i > 0; i--) {
    		i = last;
    		for (j = 1; j <= i; j++) {
    			if (arr[j-1] > arr[j]) {
    				swap(arr, j-1, j);
    				last = j;
    			}
    		}
    	}
    }
    

    选择排序

    冒泡排序每次相邻元素的比较,都可能会发生元素的交换。如果在一轮扫描中,只记录最大元素的位置,扫描结束后再判断是否交换,可以略微改善效率。

    选择排序,就是冒泡排序的简单改进版,每轮扫描都会选择一个最大元素,如果这个最大元素未就位,则交换。

    代码:

    // 选择排序,每轮的比较次数跟冒泡一样,但交换次数最多一次
    void selectionSort(int arr[], int count) {
        int i, j, key;
        for (i = count-1; i > 0; i--) {
            key = 0;
            for (j = 1; j <= i; j++) {
                if (arr[j] > arr[key]) {
                    key = j;
                }
            }
            if (i != key) {
                swap(arr, i, key);
            }
        }
    }
    

    插入排序

    玩扑克牌的时候,我们会把手里的牌排好次序。每次摸一张牌,都会插入到合适的位置。插入排序,就是这个意思。

    首先我们把待排序的元素的第一个元素看作有序集合中仅有的元素,然后把第二个元素插入到这个集合的合适位置,然后是第三个,以此类推。

    代码执行时,仍然是逐个相邻元素进行比较。每次发现逆序对,就开始执行插入操作,设逆序对前一个元素是 x,后一个元素是 y:

    1. 用临时变量 tmp 保存 y 的值。
    2. 比较 y 和 x,如果逆序,则把 x 的值放入 y 的位置
    3. 如果 x-1 未越界,则比较 x 和 x-1,如果逆序,则把 x-1 的值放入 x 的位置,然后把 x-1 看做 x
    4. 重复执行 3,直到越界或非逆序对。
    5. 把 tmp 的值赋给 x

    代码:

    // 插入排序,只扫描一轮,只要有逆序对,就将逆序对的后者与之前的元素
    // 逐个比较,直到不逆序或越界。将这些元素逐个后移一位,然后插入这个元素
    void insertSort(int arr[], int count) {
        int i, j, tmp;
        for (i = 1; i < count; i++) {
            if (arr[i-1] > arr[i]) {
                tmp = arr[i];
                j = i - 1;
                while(j >= 0 && arr[j] > tmp) {
                    arr[j+1] = arr[j];
                    j--;
                }
                arr[j+1] = tmp;
            }
        }
    }
    

    三种排序的完整代码示例

    #include <stdio.h>
    
    void swap(int arr[], int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    // 冒泡排序,每轮都会排好一个元素
    void bubbleSort(int arr[], int count) {
        int i, j;
        // n 个元素,需要比较 n-1 轮
        for (i = count - 1; i > 0; i--) {
            // 每轮比较,只需在剩余的 j 个元素中,从头开始比较 j-1 次
            for (j = 1; j <= i; j++) {
                if (arr[j-1] > arr[j]) {
                    swap(arr, j - 1, j);
                }
            }
        }
    }
    // 选择排序,每轮的比较次数跟冒泡一样,但交换次数最多一次
    void selectionSort(int arr[], int count) {
        int i, j, key;
        for (i = count-1; i > 0; i--) {
            key = 0;
            for (j = 1; j <= i; j++) {
                if (arr[j] > arr[key]) {
                    key = j;
                }
            }
            if (i != key) {
                swap(arr, i, key);
            }
        }
    }
    // 插入排序,只扫描一轮,只要有逆序对,就将逆序对的后者与之前的元素
    // 逐个比较,直到不逆序或越界。将这些元素逐个后移一位,然后插入这个元素
    void insertSort(int arr[], int count) {
        int i, j, tmp;
        for (i = 1; i < count; i++) {
            if (arr[i-1] > arr[i]) {
                tmp = arr[i];
                j = i - 1;
                while(j >= 0 && arr[j] > tmp) {
                    arr[j+1] = arr[j];
                    j--;
                }
                arr[j+1] = tmp;
            }
        }
    }
    int main(void) {
        int arr[10] = {2, 8, 1, 5, 3, 0, 4, 9, 7, 6};
        int count = 10;
        int i;
        
        printf("raw:	");
        for (i = 0; i < 10; i++) {
            printf("%d  ", arr[i]);
        }
        
        // printf("
    bubble:	");
        // bubbleSort(arr, count);
        // for (i = 0; i < 10; i++) {
        //     printf("%d  ", arr[i]);
        // }
    
        // printf("
    select:	");
        // selectionSort(arr, count);
        // for (i = 0; i < 10; i++) {
        //     printf("%d  ", arr[i]);
        // }
        
        printf("
    insert:	");
        insertSort(arr, count);
        for (i = 0; i < 10; i++) {
            printf("%d  ", arr[i]);
        }
    
        return 0;
    }
    
  • 相关阅读:
    【原】如何实现IE6下块级元素的内容自动收缩
    【原】常见的模块,你语义化了没
    【转】CSS Nuggest
    那年,寻找工作的历程
    前端开发小工具SuperApp——Ctrl+S自动刷新浏览器
    【转】在html中引入CSS的方法
    HTML中常用的实体字符
    imemode:disabled 禁止表单使用文本框输入法
    【原】工作中常用win7快捷键
    复制本地文件完整路径
  • 原文地址:https://www.cnblogs.com/kika/p/10851496.html
Copyright © 2011-2022 走看看