算法数据结构和算法分析首页投稿(暂停使用,暂停投稿)

算法和数据结构(一)—— 查找和排序

2016-08-04  本文已影响1188人  eagleRock

查找和排序都是程序设计中经常用到的算法。查找相对而言较为简单,不外乎顺序查找、二分查找、哈希表查找和二叉排序树查找。排序常见的有插入排序、冒泡排序、归并排序和快速排序。其中我们应该重点掌握二分查找归并排序快速排序,保证能随时正确、完整地写出它们的代码。同时对其他的查找和排序必须能准确说出它们的特点、对其平均时间复杂度最差时间复杂度额外空间消耗稳定性烂熟于胸。

  1. 内排序:

  2. 插入排序:直接插入排序(InsertSort)、希尔排序(ShellSort)

  3. 交换排序:冒泡排序(BubbleSort)、快速排序(QuickSort)

  4. 选择排序:直接选择排序(SelectSort)、堆排序(HeapSort)

  5. 归并排序(MergeSort)

  6. 基数排序(RadixSort)

  7. 外排序:

  8. 磁盘排序

  9. 磁带排序

  10. 查找:

  11. 线性表的查找:顺序查找、折半查找(二分查找)、索引存储结构和分块查找

  12. 树表的查找:二叉排序树、平衡二叉树、B-树、B+树

  13. 哈希表查找

快速排序(QuickSort)

    void quickSort(int[] data, int length, int start, int end) {
        if (start == end)
            return ;

        int index = partition(data, length, start, end);
        if (index > start)
            quickSort(data, length, start, index-1);
        if(index < end)
            quickSort(data, length, index+1, end);
    }

    int partition(int[] data, int length, int start, int end) {
        if (data == null || length <= 0 || start < 0 || start >= length)
            throw new RuntimeException("Invalid Parameters");
        
        int index = randomInRange(start, end);
        swap(data, index, end);
        
        int small = start - 1;
        for (index = start; index < end; index++)
            if (data[index] < data[end]) {
                ++ small;
                if (small != index)
                    swap(data, small, index);
            }
        
        ++ small;
        swap(data, small, end);
        
        return small;
    }

    int randomInRange(int start, int end) {
        return new Random().nextInt(end-start+1)+start;
    }

    void swap(int[] data, int a, int b) {
        int tmp = data[a];
        data[a] = data[b];
        data[b] = tmp;
    }

归并排序(MergeSort)

    void mergeSort(int[] data, int low, int high) {
        if (data == null || low < 0 || high >= data.length)
            throw new RuntimeException("Invalid Parameters!");

        if (low < high) {
            int mid = (low + high) / 2;
            mergeSort(data, low, mid);
            mergeSort(data, mid + 1, high);
            merge(data, low, mid, high);
        }
    }

    void merge(int[] data, int low, int mid, int high) {
        int startFrist = low, endFrist = mid, startSecond = mid + 1, endSecond = high;
        int[] tmp = new int[data.length];
        int i = startFrist, j = startSecond, index = startFrist;
        while (i <= endFrist && j <= endSecond)
            if (data[i] < data[j])
                tmp[index++] = data[i++];
            else
                tmp[index++] = data[j++];

        while (i <= endFrist)
            tmp[index++] = data[i++];

        while (j <= endSecond)
            tmp[index++] = data[j++];

        for (index = low; index <= high; index++)
            data[index] = tmp[index];
    }

二分查找

    public BinarySearch(int[] data, int length, int key) {
        if (data == null || length <= 0)
            throw new RuntimeException("Invalid parameters");

        int low = 0, high = length - 1;
        while (low <= high) {               // 当前区间存在元素时循环
            int mid = (low + high) / 2;
            if (data[mid] == key)           // 查找成功返回其逻辑序号mid+1
                return mid + 1;
            else if (data[mid] < key)       // 继续在data[mid+1 .. high]中查找
                low = mid + 1;
            else
                high = mid - 1;               // 继续在data[low .. mid-1]中查找
        }

        return 0;                           // 查找失败返回0
    }

索引存储结构和分块查找

  1. 索引存储结构

索引存储结构是在存储数据的同时,还建立附加的索引表。索引表中的每一项称为索引项,索引项的结构一般形式为(关键字,地址)。
关键字唯一标识一个节点,地址是指向该关键字对应节点的指针,也可以是相对地址。

  1. 分块查找

分块查找又称索引顺序查找,它是一种性能介于顺序查找和二分查找之间的查找方法。
分块查找需要按照如下的索引方式建立存储线性表:将表R[0.. n-1]均分为b块,前b-1块中元素个数为s=⌈n/b⌉,最后一块即第b块的元素个数等于或小于s;每一块的关键字不一定有序,但是前一块中的最大关键字必须小于后一块中的最小关键字,即要求表是“分块有序”的。
分块查找的基本思路是:首先查找索引表,因为索引表是有序表,故可以用折半查找或顺序查找,以确定待查的元素在哪一块;然后在已确定的块中进行顺序查找(因块内元素无序,只能用顺序查找)。

冒泡排序(BubbleSort)

    void bubbleSort(int[] data, int length) {
        if (data == null || length <= 0) 
            throw new RuntimeException("Invalid Paramemers");
        
        for (int i = 0; i < length-1; i++) {
            boolean isExchange = false;
            for (int j = length-1; j > i; j--) {
                if (data[j] < data[j-1]) {
                    swap(data, j, j-1);
                    isExchange = true;
                }
            }
            if(!isExchange)
                return;
        }
    }

选择排序(SelectSort)

    void selectSort(int[] data, int length) {
        if (data == null || length <= 0) 
            throw new RuntimeException("Invalid Paramemers");
        
        for (int i = 0; i < length-1; i++) {
            int minIndex = i;
            for (int j = i+1; j < length; j++) 
                if (data[j] < data[minIndex]) 
                    minIndex = j;
            if (minIndex != i) 
                swap(data, minIndex, i);
        }
    }

堆排序(heapSort)

    void heapSort(int[] data, int length) { // 为了与二叉树的顺序存储结构一致,堆排序的数据序列的下标从1开始
        if (data == null || length <= 0) 
            throw new RuntimeException("Invalid Paramemers");

        for (int i = length/2; i >= 1; i--)          //初始化堆
            sift(data, i, length);                   
        for (int i = length; i >=2; i--) {           //进行n-1趟堆排序,每一趟堆排序的元素个数减1
            swap(data, i, 1);                        //将最后一个元素同当前区间内data[1]对换
            sift(data, 1, i-1);                      //筛选data[1]节点,得到i-1个节点的堆
        }
    }

    void sift(int[] data, int low, int high) {
        int i = low, j = 2 * i;                     //data[j]是data[i]的左孩子
        int tmp = data[i];

        while (j <= high) {
            if (j < high && data[j] < data[j + 1])  //若右孩子较大,把j指向右孩子
                j++;
            if (tmp < data[j]) {
                data[i] = data[j];                  //将data[j]调整到双亲节点位置上
                i = j;                              //修改i和j值,以便继续向下筛选
                j = 2 * i;
            } else
                break;                              //筛选结束 
        }
        data[i] = tmp;                              //被筛选节点的值放入最终位置
    }

插入排序(InsertSort)

    void insertSort(int[] data, int length) {
        if (data == null || length <= 0) 
            throw new RuntimeException("Invalid Paramemers");

        for (int i = 1; i < length; i++) {
            int tmp = data[i];
            int j = i-1;                      //从右向左在有序区data[0 .. i-1]中找data[i]的插入位置
            while (j >= 0 && data[j] > tmp) {
                data[j+1] = data[j];          //将大于data[i]的元素后移
                j--;
            }
            data[j+1] = tmp;
        }
    }

希尔排序(ShellSort)

    void shellSort(int[] data, int length) {
        int gap = length / 2;
        while (gap > 0) {
            for (int i = gap; i < length; i++) {
                int tmp = data[i];
                int j = i - gap;
                while (j >= 0 && tmp < data[j]) {
                    data[j + gap] = data[j];
                    j -= gap;
                }
                data[j + gap] = tmp;
            }
            gap /= 2;
        }
    }

基数排序(RadixSort)

    class Node{
        int num;
        Node next;
    }

    void RadixSort(Node p, int r, int d) {
        if (p == null || r <= 0 || d <= 0)
            throw new RuntimeException("Invalid Parameters");

        Node[] head = new Node[10];
        Node[] tail = new Node[10];
        Node t = null;
        for (int i = 0; i <= d - 1; i++) {
            t = p;
            for (int j = 0; j < r; j++)
                head[j] = tail[j] = null;

            while (p.next != null) {
                p = p.next;
                int k = getDigit(p.num, i);
                if (head[k] == null) {
                    head[k] = p;
                    tail[k] = p;
                } else {
                    tail[k].next = p;
                    tail[k] = p;
                }
            }

            p = t;
            p.next = null;
            for (int j = 0; j < r; j++)
                if (head[j] != null) {
                    if (p.next == null)
                        p.next = head[j];
                    else
                        t.next = head[j];
                    t = tail[j];
                }
            t.next = null;
        }
    }

    public static int getDigit(int x, int d) {
        int a[] = { 1, 10, 100 }; // 如果实例的最大数是百位数,那就只要到100就可以了
        return ((x / a[d]) % 10);
    }

各种内排序方法的比较和总结

上一篇下一篇

猜你喜欢

热点阅读