【LeetCode题解】排序
1. 排序
排序(sort)是一种常见的算法,把数据根据特定的顺序进行排列。经典的排序算法如下:
- 冒泡排序(bubble sort)
- 插入排序(insertion sort)
- 选择排序(selection sort)
- 快速排序(quick sort)
- 堆排序(heap sort)
- 归并排序(merge sort)
冒泡排序依次比较相邻的两个元素,若逆序则交换;如此走访数列重复n次,即不再发生交换,排序完成。(以下图片均来自于Wikipedia)
但是,冒泡排序存在着许多无意义的交换,比如:对于基本有序的数组(最好情况),冒泡排序仍旧有\(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;
}
}
}
插入排序能很好地避免部分无意义的交换,其核心思想:从前往后扫描序列,已扫描的元素构成一个已排序的有序序列,当前扫描的元素作为待插入元素,从有序序列中找到其适合的位置进行插入。
选择排序是一种直观的排序算法,其基本思想:从前往后走访待排序序列,找出最小的元素置于待排序序列的首端;如此往复,直到待排序序列只包含一个元素。
快速排序是由Hoare提出,采用了分治(divide and conquer)策略:选取一个基准pivot,将序列划分为两个子序列,比pivot小的元素归为左子序列,比pivot大(或等于)归于右子序列;如此递归地划分子序列直到无需划分(即整体有序)。此划分操作也被称为partition;下图给出以元素5为pivot的partition操作:
堆排序是指利用大顶堆(max heap)进行排序的算法,基本思想:依次删除堆顶元素(待排序序列的最大值),将其置于待排序序列的末端;如此往复,直至堆为空。
归并排序也是采用分治策略:将相邻两个有序的子序列进行归并(merge)操作,如此往复,直到归并成一个完整序列(排序完成)。初始时,子序列对应于每一个元素。
稳定性是衡量排序算法是否改变相等键值的次序的指标。典型地,比如快排因pivot的选取可能会改变相等键值的次序。各种排序算法的比较如下:
排序算法 | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
冒泡排序 | \(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)\) | 稳定 |
2. 题解
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、1、2排序,采取类似选择排序的思路,数字0放在首端,数字2放在尾端。
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
同上求交集,不需要做去重。
【LeetCode题解】排序的更多相关文章
- 【LeetCode题解】347_前K个高频元素(Top-K-Frequent-Elements)
目录 描述 解法一:排序算法(不满足时间复杂度要求) Java 实现 Python 实现 复杂度分析 解法二:最小堆 思路 Java 实现 Python 实现 复杂度分析 解法三:桶排序(bucket ...
- [LeetCode 题解] Combination Sum
前言 [LeetCode 题解]系列传送门: http://www.cnblogs.com/double-win/category/573499.html 1.题目描述 Given a se ...
- [LeetCode题解]: Sort Colors
前言 [LeetCode 题解]系列传送门: http://www.cnblogs.com/double-win/category/573499.html 1.题目描述 Given an a ...
- LeetCode:删除排序数组中的重复项||【80】
LeetCode:删除排序数组中的重复项||[80] 题目描述 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原 ...
- 【LeetCode题解】二叉树的遍历
我准备开始一个新系列[LeetCode题解],用来记录刷LeetCode题,顺便复习一下数据结构与算法. 1. 二叉树 二叉树(binary tree)是一种极为普遍的数据结构,树的每一个节点最多只有 ...
- leetcode题解-122买卖股票的最佳时期
题目 leetcode题解-122.买卖股票的最佳时机:https://www.yanbinghu.com/2019/03/14/30893.html 题目详情 给定一个数组,它的第 i 个元素是一支 ...
- 【LeetCode题解】3_无重复字符的最长子串(Longest-Substring-Without-Repeating-Characters)
目录 描述 解法一:暴力枚举法(Time Limit Exceeded) 思路 Java 实现 Python 实现 复杂度分析 解法二:滑动窗口(双指针) 思路 Java 实现 Python 实现 复 ...
- 【LeetCode题解】225_用队列实现栈(Implement-Stack-using-Queues)
目录 描述 解法一:双队列,入快出慢 思路 入栈(push) 出栈(pop) 查看栈顶元素(peek) 是否为空(empty) Java 实现 Python 实现 解法二:双队列,入慢出快 思路 入栈 ...
- 【LeetCode题解】232_用栈实现队列(Implement-Queue-using-Stacks)
目录 描述 解法一:在一个栈中维持所有元素的出队顺序 思路 入队(push) 出队(pop) 查看队首(peek) 是否为空(empty) Java 实现 Python 实现 解法二:一个栈入,一个栈 ...
- 【LeetCode题解】844_比较含退格的字符串(Backspace-String-Compare)
目录 描述 解法一:字符串比较 思路 Java 实现 Python 实现 复杂度分析 解法二:双指针(推荐) 思路 Java 实现 Python 实现 复杂度分析 更多 LeetCode 题解笔记可以 ...
随机推荐
- 了解 : 多个Http请求设计方向 (batch)
之前都是一个restful的请求,每次只能ajax一个资源,但是遇到比较多个请求时,都是用RPC来完成,但是却让后台开了许多接口,代码开始不整齐!当然roll back只能交给RPC来负责. 游览器没 ...
- Tinywebserver:一个简易的web服务器
这是学习网络编程后写的一个练手的小程序,可以帮助复习I/O模型,epoll使用,线程池,HTTP协议等内容. 程序代码是基于<Linux高性能服务器编程>一书编写的. 首先回顾程序中的核心 ...
- Spring框架(4)---AOP讲解铺垫
AOP讲解铺垫 不得不说,刚开始去理解这个Aop是有点难理解的,主要还是新的概念比较多,对于初学者一下子不一定马上能够快速吸收,所以我先对什么事Aop做一个解释: 首先说明:本文不是自己所写 ...
- C#7.0之ref locals and returns (局部变量和引用返回,之前欠大家的,现在补上)
废话不多说,直接进入正题. 首先我们知道 ref关键字是将值传递变为引用传递 那么我们先来看看ref locals(ref局部变量) 列子代码如下: static void Main(string[] ...
- Android N安装apk报错:android.os.FileUriExposedException
StackOverflow: http://stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-s ...
- Struts2学习笔记⑥
在微信读书上在看一本李刚写的Struts 2.X权威指南 (好像叫这个)的书,可以看得出来作者的开发经验还是很充足的,但是觉得他的尺度和顺序没有把握好,他自己说拦截器是数据校验.国际化的基础-完了还把 ...
- 1712: [Usaco2007 China]Summing Sums 加密
1712: [Usaco2007 China]Summing Sums 加密 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 338 Solved: 12 ...
- js实现ajax的post请求步骤
post请求步骤与前篇的get请求步骤差别不大,只是增加了 xhr.setRequestHeader("Content-type","application/x-www- ...
- Mybatis的@Options注解
mybatis的@Options注解能够设置缓存时间,能够为对象生成自增的key 第一个使用场景: 有一个表 CREATE TABLE instance ( instance_id BIGINT UN ...
- paoding-rose 了解
paoding-rose 是人人开源的基于 spring 开发的 javaEE 框架.wiki 地址: https://code.google.com/archive/p/paoding-rose/ ...