排序(sort)是一种常见的算法,把数据根据特定的顺序进行排列。经典的排序算法以下:html
冒泡排序依次比较相邻的两个元素,若逆序则交换;如此走访数列重复n次,即再也不发生交换,排序完成。(如下图片均来自于Wikipedia)java
可是,冒泡排序存在着许多无心义的交换,好比:对于基本有序的数组(最好状况),冒泡排序仍旧有\(O(n^2)\)次交换。咱们能够标记须要交换的可能,从而下降交换次数到\(O(n)\):算法
void bubble_sort(int a[], int n) { int i, bound; int exchange = n - 1; // 初始化 while (exchange) { bound = exchange; // 记录上一次的交换位置 exchange = 0; // 假定这一次没发生交换 for (i = 0; i < bound; i++) if (a[i] > a[i + 1]) { swap(&a[i], &a[i + 1]); exchange = i; } } }
插入排序能很好地避免部分无心义的交换,其核心思想:从前日后扫描序列,已扫描的元素构成一个已排序的有序序列,当前扫描的元素做为待插入元素,从有序序列中找到其适合的位置进行插入。数组
选择排序是一种直观的排序算法,其基本思想:从前日后走访待排序序列,找出最小的元素置于待排序序列的首端;如此往复,直到待排序序列只包含一个元素。app
快速排序是由Hoare提出,采用了分治(divide and conquer)策略:选取一个基准pivot,将序列划分为两个子序列,比pivot小的元素归为左子序列,比pivot大(或等于)归于右子序列;如此递归地划分子序列直到无需划分(即总体有序)。此划分操做也被称为partition;下图给出以元素5为pivot的partition操做:
ide
堆排序是指利用大顶堆(max heap)进行排序的算法,基本思想:依次删除堆顶元素(待排序序列的最大值),将其置于待排序序列的末端;如此往复,直至堆为空。
ui
归并排序也是采用分治策略:将相邻两个有序的子序列进行归并(merge)操做,如此往复,直到归并成一个完整序列(排序完成)。初始时,子序列对应于每个元素。this
稳定性是衡量排序算法是否改变相等键值的次序的指标。典型地,好比快排因pivot的选取可能会改变相等键值的次序。各类排序算法的比较以下:spa
排序算法 | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
冒泡排序 | \(O(n^2)\) | \(T(1)\) | 稳定 |
插入排序 | \(O(n^2)\) | \(T(1)\) | 稳定 |
选择排序 | \(O(n^2)\) | \(T(1)\) | 不稳定 |
快速排序 | \(O(n \log n)\) | \(T(1)\) | 不稳定 |
堆排序 | \(O(n \log n)\) | \(T(1)\) | 不稳定 |
归并排序 | \(O(n \log n)\) | \(T(\log n)\) | 稳定 |
LeetCode题目 | 归类 |
---|---|
75. Sort Colors | |
349. Intersection of Two Arrays | 插入 |
148. Sort List | 归并 |
242. Valid Anagram | |
56. Merge Intervals | |
57. Insert Interval | |
274. H-Index | |
275. H-Index II | |
179. Largest Number | |
349. Intersection of Two Arrays | |
350. Intersection of Two Arrays II |
75. Sort Colors
数字0、一、2排序,采起相似选择排序的思路,数字0放在首端,数字2放在尾端。code
public void sortColors(int[] nums) { int low = 0, high = nums.length - 1; for (int k = 0; k <= high; k++) { if (nums[k] == 0) swap(nums, k, low++); else if (nums[k] == 2) swap(nums, k--, high--); } } private void swap(int[] A, int a, int b) { int temp = A[a]; A[a] = A[b]; A[b] = temp; }
349. Intersection of Two Arrays
链表的插入排序。
public ListNode insertionSortList(ListNode head) { ListNode nHead = new ListNode(0), p = head, pNext, np, nPre; while (p != null) { // find the suitable position to insert for (np = nHead.next, nPre = nHead; np != null && np.val < p.val; ) { np = np.next; nPre = nPre.next; } nPre.next = p; pNext = p.next; p.next = np; p = pNext; } return nHead.next; }
148. Sort List
排序链表,要求时间复杂度\(O(n \log n)\)、空间复杂度\(T(1)\),因此使用归并排序。其中,合并两个有序链表复用了问题21. Merge Two Sorted Lists的代码。
public ListNode sortList(ListNode head) { if (head == null || head.next == null) return head; ListNode pre = head, slow = head, fast = head; // cut the list into two halves while (fast != null && fast.next != null) { pre = slow; slow = slow.next; fast = fast.next.next; } pre.next = null; // sort the two halves ListNode l1 = sortList(head); ListNode l2 = sortList(slow); // merge the two sorted halves return mergeTwoLists(l1, l2); }
242. Valid Anagram
判断两个字符串是否同构(变位词)。将values数组排序后,判断是否相等。
public boolean isAnagram(String s, String t) { char[] sValues = s.toCharArray(); char[] tValues = t.toCharArray(); Arrays.sort(sValues); Arrays.sort(tValues); return Arrays.equals(sValues, tValues); }
56. Merge Intervals
合并重复的区间段。思路:排序区间,而后根据条件进行合并。
public List<Interval> merge(List<Interval> intervals) { if (intervals.size() <= 1) return intervals; intervals.sort((i1, i2) -> { if (i1.start == i2.start) return i1.end - i2.end; return i1.start - i2.start; }); List<Interval> result = new ArrayList<>(); for (int i = 0; i < intervals.size(); ) { int j, margin = intervals.get(i).end; for (j = i + 1; j < intervals.size(); j++) { if (intervals.get(j).start > margin) break; margin = Math.max(margin, intervals.get(j).end); } result.add(new Interval(intervals.get(i).start, margin)); i = j; } return result; }
57. Insert Interval
将一个区间插入到有序区间列表中,如有重复区间则须要作合并。
public List<Interval> insert(List<Interval> intervals, Interval newInterval) { LinkedList<Interval> result = new LinkedList<>(); if (intervals == null || intervals.isEmpty()) { result.add(newInterval); return result; } int len = intervals.size(), start, end, j; Interval interval; boolean hasAdded = false; for (int i = 0; i < len; ) { interval = intervals.get(i); if (interval.end < newInterval.start) { // newInterval is right-outside result.add(interval); i++; } else if (interval.start > newInterval.end) { // newInterval is left-outside if (!hasAdded) { result.add(newInterval); hasAdded = true; } result.add(interval); i++; } else { start = Math.min(interval.start, newInterval.start); end = Math.max(interval.end, newInterval.end); for (j = i + 1; j < len; j++) { interval = intervals.get(j); if (interval.start > end) break; end = Math.max(end, interval.end); } result.add(new Interval(start, end)); hasAdded = true; i = j; } } if (!hasAdded) result.add(newInterval); return result; }
274. H-Index
计算做者的h-index。思路:对引用次数数组排序,找出至少有h篇论文的引用次数>=h。
public int hIndex(int[] citations) { Arrays.sort(citations); int h = 0; for (int i = citations.length - 1; i >= 0; i--) { if (citations[i] < h + 1) break; h++; } return h; }
275. H-Index II
与上相似,不一样在于citations已排序,且是升序。
179. Largest Number
从一串数字中,找出能拼成的最大整数;至关于对整数字符串的排序。
public String largestNumber(int[] nums) { int len = nums.length; String[] strs = new String[len]; for (int i = 0; i < len; i++) { strs[i] = String.valueOf(nums[i]); } Arrays.sort(strs, this::compareNum); // special case if (strs[0].charAt(0) == '0') return "0"; StringBuilder builder = new StringBuilder(); for (String str : strs) { builder.append(str); } return builder.toString(); } // compare two number-strings private int compareNum(String num1, String num2) { String cat1 = num1 + num2; String cat2 = num2 + num1; return cat2.compareTo(cat1); }
349. Intersection of Two Arrays
求解两个数组的交集,并去重。思路:排序两个数组,而后作依次比较,用pre保存上一次添加元素(用于去重)。
public int[] intersection(int[] nums1, int[] nums2) { if (nums1 == null || nums2 == null) return null; Arrays.sort(nums1); Arrays.sort(nums2); int len1 = nums1.length, len2 = nums2.length, pre = 0; boolean hasAdded = false; ArrayList<Integer> common = new ArrayList<>(); for (int i = 0, j = 0; i < len1 && j < len2; ) { if (nums1[i] < nums2[j]) i++; else if (nums1[i] > nums2[j]) j++; else { if (nums1[i] != pre || !hasAdded) { common.add(nums1[i]); pre = nums1[i]; hasAdded = true; } i++; j++; } } int[] result = new int[common.size()]; for (int i = 0; i < common.size(); i++) { result[i] = common.get(i); } return result; }
350. Intersection of Two Arrays II 同上求交集,不须要作去重。