剑指offer(五):排序算法
- 排序算法用作实现列表的排序,列表元素可以是整数,也可以是浮点数、字符串等其他数据类型!
- 常用算法需要在排序算法的基础:
- 二分查找: 根据数组已排序的特性,才能每轮确定排除两部分中的哪一部分;
- 双指针: 例如合并两个排序链表,根据已排序特性,才能通过双指针移动在线性时间内将其合并为一个排序链表。
- 比较:
题目一:最小的 k 个数
巧妙借用快速排序的思想
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if (k >= arr.length) return arr;
return quickSort(arr, k, 0, arr.length - 1);
}
private int[] quickSort(int[] arr, int k, int l, int r) {
if(l>=r)return;
int i = l, j = r;
while (i < j) {
while (i < j && arr[j] >= arr[l]) j--;
while (i < j && arr[i] <= arr[l]) i++;
swap(arr, i, j);
}
swap(arr, i, l);
//到这一步与快速排序相比判断了k的大小
if (i > k) return quickSort(arr, k, l, i - 1);
if (i < k) return quickSort(arr, k, i + 1, r);
return Arrays.copyOf(arr, k);
}
private void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
题目二:数据流中的中位数
class MedianFinder {
Queue<Integer> A, B;
public MedianFinder() {
A = new PriorityQueue<>(); // 小顶堆,保存较大的一半
B = new PriorityQueue<>((x, y) -> (y - x)); // 大顶堆,保存较小的一半
}
public void addNum(int num) {
if(A.size() != B.size()) {
A.add(num);
B.add(A.poll());
} else {
B.add(num);
A.add(B.poll());
}
}
public double findMedian() {
return A.size() != B.size() ? A.peek() : (A.peek() + B.peek()) / 2.0;
}
}
用二叉堆实现的优先级队列,不再是FIFO而是按元素实现的Comparable接口或传入Comparator的比较结果来出队,数值越小,优先级越高,越先出队。
class Solution {
String [] strs;
public String minNumber(int[] nums) {
this.strs = new String[nums.length];
for (int i = 0; i < nums.length; i++) {
strs[i] = String.valueOf(nums[i]);
}
quicksort(strs, 0, strs.length-1);
StringBuilder list = new StringBuilder();
for (String str : strs) {
list.append(str);
}
return list.toString();
}
void quicksort(String[] strs, int l, int r){
if(l>=r)return;
int i=l,j=r;
while (i<j){
while (i<j&&(strs[j]+strs[l]).compareTo(strs[l]+strs[j])>=0)j--;
while (i<j&&(strs[i]+strs[l]).compareTo(strs[l]+strs[i])<=0)i++;
swap(strs,i,j);
}
swap(strs,l,i);
quicksort(strs,l,i-1);
quicksort(strs,i+1,r);
}
void swap(String[] strs, int i, int j){
String tem = strs[i];
strs[i] = strs[j];
strs[j] = tem;
}
}
注意点:
-
compareTo() 方法可以用来比较两个字符串的大小
- 返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的长度差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方结束。
-
借用快速排序的思想,规则比较的是字符串
题目四:扑克牌中的顺子
//方法一:排序+遍历
class Solution {
public boolean isStraight(int[] nums) {
int joker = 0;
Arrays.sort(nums); // 数组排序
for(int i = 0; i < 4; i++) {
if(nums[i] == 0) joker++; // 统计大小王数量
else if(nums[i] == nums[i + 1]) return false; // 若有重复,提前返回 false
}
// 最大牌 - 最小牌 < 5 则可构成顺子
return nums[4] - nums[joker] < 5;
}
}
//方法二:集合set+遍历
public boolean isStraight(int[] nums) {
Set<Integer> set = new HashSet<>();
int max = 0, min = 14;
for (int num : nums) {
if (num==0)continue;
max = Math.max(max,num);
min = Math.min(min, num);
if (set.contains(num))return false;
set.add(num);
}
return max-min<5;
}