十大排序算法 1. 十大排序算法 2. 工具类 3. 冒泡排序(Bubble Sort) 4. 选择排序(Selection Sort) 5. 堆排序(Heap Sort) 6. 插入排序(Insertion Sort) 7. 归并排序(Merge Sort)

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

其中 冒泡,选择,归并,快速,希尔,堆排序属于比较排序

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

稳定性理解

如果相等的两个元素,在排序前后的相对位置保持不变,那么这是稳定的排序算法。

  • 排序前:5,1,3(a),4,7,3(b)

  • 稳定的排序:1,3(a),3(b),4,5,7

  • 不稳定的排序:1,3(b),3(a),4,5,7

原地算法(In-place Algorithm)理解

定义:不依赖额外的资源或依赖少数的额外资源(空间复杂度较低),仅依靠输出覆盖输入(例如直接对输入的数组进行操作)

2. 工具类

用于提供测试数据与测试代码正确性

2.1 断言工具类

public class Asserts {
   public static void test(boolean value) {
      try {
         if (!value) throw new Exception("测试未通过");
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

2.2 Integers工具类

public class Integers {
	/** 生成随机数 */
	public static Integer[] random(int count, int min, int max) {
		if (count <= 0 || min > max) return null;
		Integer[] array = new Integer[count];
		int delta = max - min + 1;
		for (int i = 0; i < count; i++) {
			array[i] = min + (int)(Math.random() * delta);
		}
		return array;
	}

	/** 合并两个数组 */
	public static Integer[] combine(Integer[] array1, Integer[] array2) {
		if (array1 == null || array2 == null) return null;
		Integer[] array = new Integer[array1.length + array2.length];
		for (int i = 0; i < array1.length; i++) {
			array[i] = array1[i];
		}
		for (int i = 0; i < array2.length; i++) {
			array[i + array1.length] = array2[i];
		}
		return array;
		
	}

	public static Integer[] same(int count, int unsameCount) {
		if (count <= 0 || unsameCount > count) return null;
		Integer[] array = new Integer[count];
		for (int i = 0; i < unsameCount; i++) {
			array[i] = unsameCount - i;
		}
		for (int i = unsameCount; i < count; i++) {
			array[i] = unsameCount + 1;
		}
		return array;
	}

	/**
	 * 生成头部和尾部是升序的数组
	 * disorderCount:希望多少个数据是无序的
	 */
	public static Integer[] headTailAscOrder(int min, int max, int disorderCount) {
		Integer[] array = ascOrder(min, max);
		if (disorderCount > array.length) return array;
		
		int begin = (array.length - disorderCount) >> 1;
		reverse(array, begin, begin + disorderCount);
		return array;
	}

	/**
	 * 生成中间是升序的数组
	 * disorderCount:希望多少个数据是无序的
	 */
	public static Integer[] centerAscOrder(int min, int max, int disorderCount) {
		Integer[] array = ascOrder(min, max);
		if (disorderCount > array.length) return array;
		int left = disorderCount >> 1;
		reverse(array, 0, left);
		
		int right = disorderCount - left;
		reverse(array, array.length - right, array.length);
		return array;
	}

	/**
	 * 生成头部是升序的数组
	 * disorderCount:希望多少个数据是无序的
	 */
	public static Integer[] headAscOrder(int min, int max, int disorderCount) {
		Integer[] array = ascOrder(min, max);
		if (disorderCount > array.length) return array;
		reverse(array, array.length - disorderCount, array.length);
		return array;
	}

	/**
	 * 生成尾部是升序的数组
	 * disorderCount:希望多少个数据是无序的
	 */
	public static Integer[] tailAscOrder(int min, int max, int disorderCount) {
		Integer[] array = ascOrder(min, max);
		if (disorderCount > array.length) return array;
		reverse(array, 0, disorderCount);
		return array;
	}

	/** 升序生成数组 */
	public static Integer[] ascOrder(int min, int max) {
		if (min > max) return null;
		Integer[] array = new Integer[max - min + 1];
		for (int i = 0; i < array.length; i++) {
			array[i] = min++;
		}
		return array;
	}

	/** 降序生成数组 */
	public static Integer[] descOrder(int min, int max) {
		if (min > max) return null;
		Integer[] array = new Integer[max - min + 1];
		for (int i = 0; i < array.length; i++) {
			array[i] = max--;
		}
		return array;
	}
	
	/** 反转数组 */
	private static void reverse(Integer[] array, int begin, int end) {
		int count = (end - begin) >> 1;
		int sum = begin + end - 1;
		for (int i = begin; i < begin + count; i++) {
			int j = sum - i;
			int tmp = array[i];
			array[i] = array[j];
			array[j] = tmp;
		}
	}

	/** 复制数组 */
	public static Integer[] copy(Integer[] array) {
		return Arrays.copyOf(array, array.length);
	}

	/** 判断数组是否升序 */
	public static boolean isAscOrder(Integer[] array) {
		if (array == null || array.length == 0) return false;
		for (int i = 1; i < array.length; i++) {
			if (array[i - 1] > array[i]) return false;
		}
		return true;
	}

	/** 打印数组 */
	public static void println(Integer[] array) {
		if (array == null) return;
		StringBuilder string = new StringBuilder();
		for (int i = 0; i < array.length; i++) {
			if (i != 0) string.append("_");
			string.append(array[i]);
		}
		System.out.println(string);
	}
}

2.3 时间测试工具类

public class Times {
	private static final SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss.SSS");
	
	public interface Task {
		void execute();
	}
	
	public static void test(String title, Task task) {
		if (task == null) return;
		title = (title == null) ? "" : ("【" + title + "】");
		System.out.println(title);
		System.out.println("开始:" + fmt.format(new Date()));
		long begin = System.currentTimeMillis();
		task.execute();
		long end = System.currentTimeMillis();
		System.out.println("结束:" + fmt.format(new Date()));
		double delta = (end - begin) / 1000.0;
		System.out.println("耗时:" + delta + "秒");
		System.out.println("-------------------------------------");
	}
}

2.4 Sort抽象父类

public abstract class Sort<T extends Comparable<T>> implements Comparable<Sort<T>> {
    /** 目标数组 */
    protected T[] array;
    /** 比较次数 */
    private int cmpCount;
    /** 交换次数 */
    private int swapCount;
    /** 执行时间 */
    private long time;
    /** 小数格式化 */
    private DecimalFormat fmt = new DecimalFormat("#.00");

    /** 预处理 */
    public void sort(T[] array) {
        if (array == null || array.length < 2) return;
        this.array = array;
        long begin = System.currentTimeMillis();
        sort();
        time = System.currentTimeMillis() - begin;
    }

    /** 目标方法 */
    protected abstract void sort();

    /**
     * 比较数组下标对应的值
     *
     * 返回值等于0,代表 array[index1] == array[index2]
     * 返回值小于0,代表 array[index1] < array[index2]
     * 返回值大于0,代表 array[index1] > array[index2]
     */
    protected int cmp(int index1, int index2) {
        cmpCount++;
        return array[index1].compareTo(array[index2]);
    }

    /** 比较值 */
    protected int cmp(T value1, T value2) {
        cmpCount++;
        return value1.compareTo(value2);
    }

    /** 交换值 */
    protected void swap(int index1, int index2) {
        swapCount++;
        T tmp = array[index1];
        array[index1] = array[index2];
        array[index2] = tmp;
    }

    /** 稳定性测试 */
    @SuppressWarnings("unchecked")
    private boolean isStable() {
        Student[] students = new Sort.Student[20];
        for (int i = 0; i < students.length; i++) {
            //(0,10) (10,10) (20,10) (30,10)
            students[i] = new Student(i * 10, 10);
        }
        sort((T[]) students);//只会对年龄进行排序
        for (int i = 1; i < students.length; i++) {
            int score = students[i].score;
            int prevScore = students[i - 1].score;
            if (score != prevScore + 10) return false;
        }
        return true;
    }

    private static class Student implements Comparable<Student>{
        Integer score;
        Integer age;
        public Student(Integer score, Integer age) {
            this.score = score;
            this.age = age;
        }

        @Override
        public int compareTo(Student o) {
            return age - o.age;
        }
    }

    /** 排序方式 */
    @Override
    public int compareTo(Sort o) {
        int result = (int)(time - o.time);
        if(result != 0) return result;
        result = cmpCount - o.cmpCount;
        if(result != 0) return result;
        return swapCount - o.swapCount;
    }

    @Override
    public String toString() {
        return "【" + getClass().getSimpleName() + "】
"
                + "交换次数 ==> " + numberString(swapCount) + "
"
                + "比较次数 ==> " + numberString(cmpCount) + "
"
                + "执行时间 ==> " + time * 0.001 + "s" + "
"
                + "稳定性 ==> " + isStable() + "
"
                + "=================================";
    }

    /** 数字格式化 */
    private String numberString(int number) {
        if (number < 10000) return "" + number;

        if (number < 100000000) {
            return fmt.format(number / 10000.0) + "万";
        }
        return fmt.format(number / 100000000.0) + "亿";
    }

}

3. 冒泡排序(Bubble Sort)

3.1 执行流程

  • 从头开始比较每一对相邻元素,如果第一个比第二个大就交换它们的位置。执行完一轮后最末尾哪个元素就是最大的元素
  • 忽略第一步找到的最大元素,重复执行第一步,直到全部元素有序
十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

3.2 基本实现

public void sort() {
    for (int eIndex = array.length - 1; eIndex > 0; eIndex--) {
        for (int i = 1; i <= eIndex; i++) {
            if (cmp(i, i - 1) < 0) {
                swap(i, i - 1);
            }
        }
    }
}

3.4 优化一

优化方案:如果序列已经完全有序,可以提前终止冒泡排序

缺点:只有当完全有序时才会提前终止冒泡排序,概率很低

public void sort() {
    for (int eIndex = array.length - 1; eIndex > 0; eIndex--) {
        boolean sorted = true;
        for (int i = 1; i <= eIndex; i++) {
            if (cmp(i,i - 1) < 0) {
                swap(i, i - 1);
                sorted = false;
            }
        }
        if (sorted) break;
    }
}

3.5 优化二

优化方案:如果序列尾部已经局部有序,可以记录最后一次交换的位置,减少比较次数

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)
public class BubbleSort<T extends Comparable<T>> extends Sort<T> {
    /**
     *  优化方式二:如果序列尾部已经局部有序,可以记录最后依次交换的位置,减少比较次数
     *  为什么这里sortedIndex为1(只要保证 eIndex-- > 0 即可)?
     *     => 如果sortedIndex为eIndex,当数组第一次就完全有序时,就退回到最初的版本了
     *     => 如果sortedIndex为1,当数组第一次就完全有序时,一轮扫描就结束了!
     * 
     */
    @Override
    public void sort() {
        for (int eIndex = array.length - 1; eIndex > 0; eIndex--) {
            int sortedIndex = 1; //记录最后一次交换的下标位置
            for (int i = 1; i <= eIndex; i++) {
                if (cmp(i, i - 1) < 0) {
                    swap(i, i - 1);
                    sortedIndex = i;
                }
            }
            eIndex = sortedIndex;
        }
    }
}

3.6 算法优劣

  • 最坏,平均时间复杂度:O(n^2),最好时间复杂度:O(n)

  • 空间复杂度:O(1)

  • 属于稳定排序

注意:稍有不慎,稳定的排序算法也能被写成不稳定的排序算法,如下冒泡排序是不稳定的

public void sort() {
    for (int eIndex = array.length - 1; eIndex > 0; eIndex--) {
        for (int i = 1; i <= eIndex; i++) {
            if (cmp(i, i - 1) <= 0) {
                swap(i, i - 1);
            }
        }
    }
}
  • 属于原地算法

4. 选择排序(Selection Sort)

4.1 执行流程

  • 从序列中找出最大的哪个元素,然后与最末尾的元素交换位置。执行完一轮后最末尾那个元素就是最大的元素
  • 忽略第一步找到的最大元素,重复执行第一步

这里以选最小元素为例

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

4.2 基本实现

public class SelectionSort<T extends Comparable<T>> extends Sort<T> {
    @Override
    public void sort() {
        for (int eIndex = array.length - 1; eIndex > 0; eIndex--) {
            int maxIndex = 0;
            for (int i = 1; i <= eIndex; i++) {
                //注意:为了稳定性,这里要写 <=
                if (cmp(maxIndex, i) <= 0) {
                    maxIndex = i;
                }
            }
            if(maxIndex != eIndex) swap(maxIndex, eIndex);
        }
    }

}

4.3 算法优劣

  • 选择排序的交换次数要远少于冒泡排序,平均性能优于冒泡排序
  • 最好,最坏,平均时间复杂度均为O(n^2),空间复杂度为O(1),属于不稳定排序

选择排序是否还有优化的空间? => 使用堆来选择最大值

5. 堆排序(Heap Sort)

堆排序可以认为是对选择排序的一种优化

5.1 执行流程

  • 对序列进行原地建堆(heapify)
  • 重复执行以下操作,直到堆的元素数量为1
    • 交换堆顶元素与尾元素
    • 堆的元素数量减1
    • 对0位置进行一次siftDown操作
十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

5.2 基本实现

public class HeapSort<T extends Comparable<T>> extends Sort<T> {
    /** 记录堆数据 */
    private int heapSize;

    @Override
    protected void sort() {
        // 原地建堆(直接使用数组建堆)
        heapSize = array.length;
        for (int i = (heapSize >> 1) - 1; i >= 0; i--) {
            siftDown(i);
        }
        while (heapSize > 1) {
            // 交换堆顶元素和尾部元素
            swap(0, --heapSize);

            // 对0位置进行siftDown(恢复堆的性质)
            siftDown(0);
        }
    }

    /** 堆化 */
    private void siftDown(int index) {
        T element = array[index];

        int half = heapSize >> 1;
        while (index < half) { // index必须是非叶子节点
            // 默认是左边跟父节点比
            int childIndex = (index << 1) + 1;
            T child = array[childIndex];

            int rightIndex = childIndex + 1;
            // 右子节点比左子节点大
            if (rightIndex < heapSize &&
                    cmp(array[rightIndex], child) > 0) {
                child = array[childIndex = rightIndex];
            }

            // 大于等于子节点
            if (cmp(element, child) >= 0) break;

            array[index] = child;
            index = childIndex;
        }
        array[index] = element;
    }
}

5.2 算法优劣

  • 最好,最坏,平均时间复杂度:O(nlog^n)

  • 空间复杂度:O(1)

  • 属于不稳定排序

5.3. 冒泡,选择,堆排序比较

@SuppressWarnings({"rawtypes","unchecked"})
public class SortTest {
    public static void main(String[] args) {
        Integer[] arr1 = Integers.random(10000, 1, 20000);
        testSort(arr1,
                new SelectionSort(),
                new HeapSort(),
                new BubbleSort());

    }

    static void testSort(Integer[] arr,Sort... sorts) {
        for (Sort sort: sorts) {
            Integer[] newArr = Integers.copy(arr);
            sort.sort(newArr);
            //检查排序正确性
            Asserts.test(Integers.isAscOrder(newArr));
        }
        Arrays.sort(sorts);
        for (Sort sort: sorts) {
            System.out.println(sort);
        }
    }
}
十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

6. 插入排序(Insertion Sort)

6.1 执行流程

  • 在执行过程中,插入排序会将序列分为两部分(头部是已经排好序的,尾部是待排序的)

  • 从头开始扫描每一个元素,每当扫描到一个元素,就将它插入到头部适合的位置,使得头部数据依然保持有序

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

6.2 基本实现

public class InsertionSort<T extends Comparable<T>> extends Sort<T> {
    @Override
    protected void sort() {
        for (int i = 1; i < array.length; i++) {
            int cur = i;
            while(cur > 0 && cmp(cur,cur - 1) < 0) {
                swap(cur,cur - 1);
                cur--;
            }
        }
    }
}

6.3 逆序对(Inversion)

什么是逆序对? => 数组 [2,3,8,6,1] 的逆序对为:<2,1> < 3,1> <8,1> <8,6> <6,1>

插入排序的时间复杂度与逆序对的数量成正比关系

时间复杂度最高如下:O(n^2)

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

6.4 优化一

优化思路 => 将交换改为挪动

  • 先将待插入元素备份

  • 头部有序数据中比待插入元素大的,都朝尾部方向挪动1个位置

  • 将待插入元素放到最终合适位置

注意:逆序对越多,该优化越明显

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)
public class InsertionSort<T extends Comparable<T>> extends Sort<T> {
    @Override
    protected void sort() {
        for (int i = 1; i < array.length; i++) {
            int cur = i;
            T val = array[cur];
            while(cur > 0 && cmp(val,array[cur - 1]) < 0) {
                array[cur] = array[cur - 1];//优化重点在这里
                cur--;
            }
            array[cur] = val;
        }
    }
}

6.5 优化二

优化思路 => 将交换改为二分搜索(较少比较次数)

二分搜索理解

如何确定一个元素在数组中的位置?(假设数组里全是整数)

  • 如果是无序数组,从第 0 个位置开始遍历搜索,平均时间复杂度:O(n)

  • 如果是有序数组,可以使用二分搜索,最坏时间复杂度:O(log^n)

思路

  • 如下,假设在 [begin, end) 范围内搜索某个元素 v,mid == (begin + end) / 2
  • 如果 v < mid,去 [begin,mid) 范围内二分搜索
  • 如果 v > mid,去 [mid + 1,end) 范围内二分搜索
  • 如果 v == mid,直接返回 mid
十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

实例

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)
/** 二分搜索-基本实现
 *      查找val在有序数组arr中的位置,找不到就返回-1
 */
private static int indexOf(Integer[] arr,int val) {
    if(arr == null || arr.length == 0) return -1;
    int begin = 0;
    //注意这里end设计为arr.length便于求数量(end - begin)
    int end = arr.length;
    while (begin < end) {
        int mid = (begin + end) >> 1;
        if(val < arr[mid]) {
            end = mid;
        } else if(val > arr[mid]) {
            begin = mid  + 1;
        } else {
            return mid;
        }
    }
    return -1;
}

二分搜索(Binary Search)优化实现

  • 之前的插入排序代码,在元素 val 的插入过程中,可以先二分搜索出合适的插入位置,然后将元素 val 插入
  • 适合于插入排序的二分搜索必须满足:要求二分搜索返回的插入位置是第1个大于 val 的元素位置
    • 如果 val 是 5 ,返回 2
    • 如果 val 是 1,返回 0
    • 如果 val 是15,返回 7
    • 如果 val 是 8,返回 5
十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)
  • 实现思路
    • 假设在 [begin,end) 范围内搜索某个元素 val,mid == (begin + end) / 2
    • 如果val < mid,去 [begin,mid) 范围内二分搜索
    • 如果val >= mid,去 [mid + 1,end) 范围内二分搜索
    • 当 begin == end == x,x 就是待插入位置
  • 实例
十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)
/**
 * 二分搜索-适用于插入排序
 *    查找val在有序数组arr中可以插入的位置
 *    规定:要求二分搜索返回的插入位置是第1个大于 val 的元素位置
 */
private static int search(Integer[] arr,int val) {
    if(arr == null || arr.length == 0) return -1;
    int begin = 0;
    int end = arr.length;
    while (begin < end) {
        int mid = (begin + end) >> 1;
        if(val < arr[mid]) {
            end = mid;
        } else {
            begin = mid  + 1;
        }
    }
    return begin;
}

插入排序最终实现

注意:使用了二分搜索后,只是减少了比较次数,但插入排序的平均时间复杂度依然是O(n^2)

public class InsertionSort<T extends Comparable<T>> extends Sort<T> {
 
    /** 优化 => 二分搜索 */
    @Override
    protected void sort() {
        for (int begin = 1; begin < array.length; begin++) {
            //这里为什么传索引而不是传值?
            // => 传索引还可以知道前面已经排好序的数组区间:[0,i)
            insert(begin,search(begin));
        }
     }

    /** 将source位置的元素插入到dest位置 */
    private void insert(int source,int dest) {
         //将[dest,source)范围内的元素往右边挪动一位
         T val = array[source];
         for (int i = source; i > dest; i--) {
             array[i] = array[i - 1];
         }
         //插入
         array[dest] = val;
    }

    private int search(int index) {
        T val = array[index];
        int begin = 0;
        int end = index;
        while (begin < end) {
            int mid = (begin + end) >> 1;
            if(cmp(val,array[mid]) < 0) {
                end = mid;
            } else {
                begin = mid  + 1;
            }
        }
        return begin;
    }
}

6.6 算法优劣

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)
  • 最坏,平均时间复杂度为 O(n^2),最好时间复杂度为 O(n)
  • 空间复杂度为 O(1)
  • 属于稳定排序

7. 归并排序(Merge Sort)

7.1 执行流程

  • 不断的将当前序列平均分割成 2 个子序列,直到不能再分割(序列中只剩一个元素)
  • 不断的将 2 个子序列合并成一个有序序列,直到最终只剩下 1 个有序序列

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

7.2 思路

merge

大致想法

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

细节

  • 需要 merge 的 2 组序列存在于同一个数组中,并且是挨在一起的
十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)
  • 为了更好的完成 merge 操作,最好将其中 1 组序列备份出来,比如 [begin,mid)
十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)
  • 基本实现
十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)
  • 情况一:左边先结束 => 左边一结束整个归并就结束
十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)
  • 情况二:右边先结束 => 右边一结束就直接将左边按顺序挪到右边去
十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

7.3 基本实现

@SuppressWarnings("unchecked")public class MergeSort<T extends Comparable<T>> extends Sort<T> {    private T[] leftArr;    @Override    protected void sort() {        leftArr = (T[]) new Comparable[array.length >> 1];        sort(0, array.length);    }    /** 对 [begin,end) 位置的元素进行归并排序 */    private void sort(int begin, int end) {        if (end - begin < 2) return;        int mid = (begin + end) >> 1;        sort(begin, mid);        sort(mid, end);        merge(begin, mid, end);    }    /** 将 [begin,mid) 和 [mid,end) 范围的序列合并成一个有序序列 */    private void merge(int begin, int mid, int end) {        int li = 0, le = mid - begin;        int ri = mid, re = end;        int ai = begin;        //备份左边数组        for (int i = 0; i < le; i++) {            leftArr[i] = array[begin + i];        }        //如果左边还没有结束(情况一)        while (li < le) {            //当 ri < re 不成立,就会一直leftArr挪动(情况二)            if (ri < re && cmp(array[ri],leftArr[li]) < 0) {                array[ai++] = array[ri++];            } else { //注意稳定性                array[ai++] = leftArr[li++];            }        }    }}

7.4 算法优劣

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

复杂度分析

T(n) = sort() + sort() + merge()=> T(n) = T(n/2) + T(n/2) + O(n)=>  T(n) = 2T(n/2) + O(n)    //由于sort()是递归调用,用T表示,由于T(n/2)不好估算,现在要理清T(n)与O(n)之间的关系T(1) = O(1)T(n)/n = T(n/2) / (n/2) + O(1)    //令S(n) = T(n)/n     S(1) = O(1) S(n) = S(n/2) + O(1)      = S(n/4) + O(2)     = S(n/8) + O(3)     = S( n/(2^k) ) + O(k)     = S(1) + O(log^n)     = O(lon^n)T(n) = n*S(n) = O(nlog^n)    => 归并排序时间复杂度:O(nlog^n)

常见递推式

十大排序算法
1. 十大排序算法
2. 工具类
3. 冒泡排序(Bubble Sort)
4. 选择排序(Selection Sort)
5. 堆排序(Heap Sort)
6. 插入排序(Insertion Sort)
7. 归并排序(Merge Sort)

总结

  • 由于归并排序总是平均分割子序列,所以最好,最坏,平均时间复杂度都是:O(nlog^n)

  • 空间复杂度:O(n/2 + log^n) = O(n),n/2用于临时存放左侧数组,log^n用于递归调用

  • 属于稳定排序