zoukankan      html  css  js  c++  java
  • 面试题算法与排序(转)

    关键字: 算法---排序

    排序的关键字

    1. 时间复杂度:整个排序算法运行所需要的时间。

    2. 空间复杂度:排序算法运行过程汇总所需要额外空间

    3. 稳定性:若待排的序列中有大小相同的两个数,若整个排序过程中不存在两数次序交换的可能新内阁,则该排序算法是稳定的。

    4. in-place:算法使用的额外存储空间是常数级的

    一,最基本的冒泡排序——Bubble Sort。

    public void swap(int[] data, int i, int j) { if (i != j) {
            data[i] = data[i] + data[j];
            data[j] = data[i] - data[j];
            data[i] = data[i] - data[j];
        }
    }

    二,冒泡排序(递增)

    冒泡排序,是所有排序中最简单的一种,也是效率最低的一种,时间复杂度O(n2),空间复杂度O(n)。冒泡排序没有改变原始元素的相对位置,因此是稳定的排序。

    public void bubble_sort(int[] data) {
    for (int i = 0; i < data.length; i++) {
    for (int j = 0; j < data.length; j++) {
    if (data[j] > data[j + 1]) {
                    swap(data, j, j + 1);
                }
            }
        }
    }

    三,插入排序(递增)

    插入排序也是一种比较简单的排序方法,它的基本原理就好似我们打牌过程中摸牌理牌那一环,当你摸到一张牌后将其插入到合适的位置。

    插入排序首先定位一个数(一般从第二个开始),将这个数依次与位于它之前的数进行比较,经过一轮比较,找到它在这些数中适当的位置。然后定位下一个数,再找到合适的为止,依次进行直到最后一个数。

    例如(5 2 1 4 3),黑体为进行交换的两数。

    • 第一轮:

      2 5 1 4 3)

    • 第二轮:

      (2 1 5 4 3)

      1 2 5 4 3)

    • 第三轮:

      (1 2 4 5 3)

      (1 2 4 5 3)

      (1 2 4 5 3)

    • 第四轮:

      (1 2 4 3 5

      (1 2 3 4 5)

      (1 2 3 4 5)

      (1 2 3 4 5)排序完成

    public void insertion_sort(int[] data) {
    int key = 0;
    int j = 0;
    for (int i = 1; i < data.length; i++) {
            key = data[i];
            j = i - 1;
    while (j >= 0 && data[j] > key) {
                swap(data, j, j + 1);
                j = j - 1;
            }
        }
    }

    排序插入在数据集较大的时候效率会变得恨低,但是它易于实现,处理小型数据集时效率较高,同时也是稳定的,in-place的,它的时间复杂度是O(n2),空间复杂度是O(n)。

    四,选择排序

    选择排序的工作原理

    1. 找到数据集中的最小元素

    2. 将最小元素与未排序声誉元素的第一个元素交换

    3. 对剩余元素进行以上步骤

    它的时间复杂度是O(n2),空间复杂度是O(n),同插入排序类似,它也不适用于大数据集。但是它易于实现,也是一种in-place的排序算法。对于稳定性:简易实现是不稳定的,例如(3 5 5 2),在第二轮中第二个五会被认为是最小的,然后同第一个五进行交换。

    public void selection_sort(int[] data) {
    int minimum = 0;
    for (int i = 0; i < data.length - 1; i++) {
            minimum = i;
    for (int j = i + 1; j < data.length; j++) {
    if (data[j] < data[minimum]) {
                    minimum = j;
                }
            }
            swap(data, i, minimum);
        }
    }

    五,ELFHash

    public int ELFHash(String str, int number) {
        int hash = 0;
        long x = 0L;
        char[] array = str.toCharArray();
        for (int i = 0; i < array.length; i++) {
            hash = (hash << 4) + array[i];
            if ((x = (hash & 0xF0000000L)) != 0) {
                hash ^= (x >> 24);
                hash &= ~x;
            }
        }
        int result = (hash & 0x7FFFFFFF) % number;
        return result;
    }

    六,快速排序

    快速排序的步骤:

    1. 从数组中选出一个中枢数(pivot)

    2. 重新排列该数组,让数组中比该数小的数都排在该数的前面,比该数大的数都排在该数的后面。经过这次排序,该数处于其最终为止,并将原数组分为两个子数组(大于它的数组和小于它的数组),这就是分段的过程。

    3. 递归的排列各个子数组,直至最后整个数组排序完成。

    快速排序的平均时间复杂度为O(nlogn),空间复杂度依据各种实现方式有所不同。

    public int partition(int[] data, int left, int right, int pivotIndex) {
    int privotValue = data[pivotIndex];
        swap(data, pivotIndex, right); // Move pivot to end
    int storeIndex = left;
    for (int i = left; i < right; i++) {
    if (data[i] <= privotValue) {
                swap(data, i, storeIndex);
                storeIndex = storeIndex + 1;
             }
        }
        swap(data, storeIndex, right); // Move pivot to its final place
    return storeIndex;
    }

    public void quick_sort(int[] data, int left, int right) {
    if (right > left) {
    int pivotIndex = left;
    int pivotNewIndex = partition(data, left, right, pivotIndex);
            quick_sort(data, index, pivotNewIndex - 1);
            quick_sort(data, pivotNewIndex + 1, right);
        }
    }

    七,归并排序

    归并排序是一种基于比较的排序算法,在多数的实现方法中它是稳定的。归并排序可是由计算机祖师级人物——冯诺依曼提出的哦。

    归并排序的过程:

    1. 如果数据链表的长度为0或1,则返回

    2. 将原始数据链表对半分成两个子链表

    3. 对每个子链表递归的调用合并排序进行排序

    4. 合并两个子链表使其成为一个排序完成的链表

    归并排序的时间复杂度为O(nlogn),空间复杂度为O(n)。

    public List<Integer> mergesort(List<Integer> data) {
    if (data.size() <= 1) {
    return data;
        }
    int middle = data.size() / 2;

    List<Integer> left = new ArrayList<Integer>();
        List<Integer> right = new ArrayList<Integer>();
    for (int i = 0; i < middle; i++) {
            left.add(data.get(i));
        }
    for (int i = middle; i < data.size(); i++) {
            right.add(data.get(i));
        }
        left = mergesort(left);
        right = mergesort(right);
        List<Integer> result = merge(left, right);
    return result;
    }

    public List<Integer> merge(List<Integer> left, List<Integer> right) {
        List<Integer> result = new ArrayList<Integer>();
    while (left.size() > 0 && right.size() > 0) {
    if (((Integer)left.get(0)).intValue() <= ((Integer)right.get(0)).intValue()) {
                result.add(left.get(0));
                left.remove(0);
            } else {
                result.add(right.get(0));
                right.remove(0);
            }
        }
    if (left.size() > 0) {
    for (Iterator<Integer> iter = left.iterator(); iter.hasNext();) {
                result.add(iter.next());
            }
        }
    if (right.size() > 0) {
    for (Iterator<Integer> iter = right.iterator(); iter.hasNext();) {
                result.add(iter.next());
            }
        }
    return result;
    }

    八,堆排序

    堆排序是一种基于比较的排序算法,它比实现的较好的快速排序慢一些,但是它的平均时间复杂度为O(nlogn),空间复杂度为O(n),它是一种in-place的算法,但是确实不稳定的排序算法。

    最大堆和最小堆的定义:

    根结点(亦称为堆顶)的关键字是堆里所有节点关键字中最小者的堆成为最小堆。

    根结点(亦称为堆顶)的关键字是堆里所有节点关键字中最大者的堆成为最大堆。

    P.S.:

    堆中任一子树亦是堆。本文讨论的堆实际上是二插堆(Binary Heap),类似地可以定义k叉堆。

    堆排序的过程:

    1. 根据输入的数据集建立一个最大堆(最小堆)

    2. 进行堆排序,将Root(最大值)与堆的最后一个元素交换

    3. 堆调整,继续维护成为最大堆

    4. 进行步骤2和3,直至排序完成

    public void siftDown(int[] data, int start, int end) {
    int root = start;
    while ((2 * root + 1) <= end) {
    int child = root * 2 + 1;
    int (child < end && data[child] < data[child + 1]) {
                child++;
            }
    if (data[root] < data[child]) {
                swap(data, root, child);
                root = child;
            } else {
    break;
            }
        }
    }
    这段代码是堆排序的核心,对堆中的元素进行调整。简单来说做的工作就是,即针对一个堆点,将其与它孩子中较大的那个进行比较,若大不变,若小与该孩子交换位置,若交换后该堆点(处于原先它孩子的位置)仍有孩子则继续与孩子中较大的那个进行比较,若大不变,若下与该孩子交换位置,调整直至该堆点没有孩子结束。

    public void heapify(int[] data, int count) {
    int start = (count - 1) /2;
    while (start >= 0) {
            siftDown(data, start, count - 1);
            start = start - 1;
        }
    }

    这段代码是建堆的过程,找到最后一个有孩子的堆点,对该堆点进行调整,直至调整到Root。

    public void heapsort(int[] data, int count) {
        heapify(data, count);
    int end = count - 1;
    while (end > 0) {
            swap(data, 0, end);
            siftDown(data, 0, --end);
        }
    }

    这段代码解释了堆排序的过程,首先建堆,然后将Root与堆底元素交换,继而调整现有堆中Root(交换后的Root)位置,不断的调整直至遍历完整个堆。


    作者:高级测试开发网
    博客地址:https://seniortesting.club
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    java_oop_方法2
    POJ 3276 Face The Right Way(反转)
    POJ 3276 Face The Right Way(反转)
    POJ 2566 Bound Found(尺取法,前缀和)
    POJ 2566 Bound Found(尺取法,前缀和)
    POJ 3320 Jessica's Reading Problem(尺取法)
    POJ 3320 Jessica's Reading Problem(尺取法)
    POJ 3061 Subsequence(尺取法)
    POJ 3061 Subsequence(尺取法)
    HDU 1222 Wolf and Rabbit(欧几里得)
  • 原文地址:https://www.cnblogs.com/seniortestingdev/p/2384341.html
Copyright © 2011-2022 走看看