排序(sort)
1 排序
这里是排序常见的题目,包括计数、hash以及用到内置Arrays.sort()函数的排序;
也包括一些快速排序、归并排序的应用。
对于常用排序算法的总结会在另外一篇博文中详细介绍!!
1.1 (lee-56) 合并区间
给出一个区间的集合,请合并所有重叠的区间。
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]
思路
先排序: 按照每个区间的左端点进行升序排序,因为可以合并的区间一定是连续的;[1,3][2,6][8,10][15,18]
遍历区间:初始区间的左右端点设为第一个区间 L = [0][0],R = [0][1],当下一个区间的左端点 <= 当前区间的右端点 ,并且下一区间的右端点 >= 当前区间的右端点,说明当前区间可以和下一区间合并,因此取当前区间的左端点和下一区间的右端点为新的区间;
依次类推。
//时间复杂度:O(nlogn)
//空间复杂度:O(logn)
public int[][] merge(int[][] intervals) {
List<int[]> res = new ArrayList<>();
if(intervals.length == 0) {
return new int[0][2];
}
Arrays.sort(intervals,new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
int len = intervals.length;
for(int i = 0;i < len;i++) {
int left = intervals[i][0];
int right = intervals[i][1];
while(i < len - 1 && intervals[i+1][0] <= right) {
i++;
right = Math.max(right, intervals[i][1]);
}
res.add(new int[] {left,right});
}
return res.toArray(new int[0][1]); //new int[0][];
}
1.2 (lee-49) 字母异位词分组
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
说明:所有输入均为小写字母。不考虑答案输出的顺序。
输入: [“eat”, “tea”, “tan”,“ate”,“nat”,“bat”]
输出: [ [“ate”,“eat”,“tea”], [“nat”,“tan”], [“bat”]]
(1) 排序+hash表
Arrays.sort()
/**
* 1.排序+hash表
* 思路:由于互为字母异位词的两个字符串包含的字母相同,因此对两个字符串分别进行排序之后得到的字符串一定是相同的,故可以将排序之后的字符串作为哈希表的键。
* 使用哈希表存储每一组字母异位词,哈希表的键为一组字母异位词的标志,哈希表的值为一组字母异位词列表。
* @param strs
* @return
*/
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> res = new ArrayList<>();
Map<String,List<String>> map = new HashMap<>();
for (String str : strs) {
char[] arr = str.toCharArray(); //将每个字符串转换成字符数组
Arrays.sort(arr); //对每个字符数组进行排序
String key = new String(arr); //将排好序的字符数组转换为字符串
List<String> list = map.getOrDefault(key, new ArrayList<>());
list.add(str);
map.put(key, list);
}
res.addAll(map.values());
return res;
}
(2)计数+hash表
/**
* 2.计数+hash表
* 由于互为字母异位词的两个字符串包含的字母相同,因此两个字符串中的相同字母出现的次数一定是相同的,
* 故可以将每个字母出现的次数使用字符串表示,作为哈希表的键。
* 由于字符串只包含小写字母,因此对于每个字符串,可以使用长度为26的数组记录每个字母出现的次数。
*/
public List<List<String>> groupAnagrams1(String[] strs) {
if(strs.length == 0) {
return new ArrayList<>();
}
List<List<String>> res = new ArrayList<>();
Map<String,List<String>> map = new HashMap<>();
int[] count = new int[26];
for(String s : strs) {
Arrays.fill(count, 0); //填充count数组中的每个元素都是0
for(char c : s.toCharArray()) {
count[c - 'a']++;
}
StringBuilder sb = new StringBuilder("");
for(int i = 0;i <26;i++) {
sb.append('#');
sb.append(count[i]);
}
String key = sb.toString();
if(!map.containsKey(key)) {
map.put(key, new ArrayList<>());
}
map.get(key).add(s);
}
res.addAll(map.values());
return res;
}
1.3 (lee-75) 颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
快速排序
思路:要求原地排序,所以用快排和分治都行
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
public void sortColors(int[] nums) {
quickSort(nums, 0, nums.length - 1);
}
public static void quickSort(int[] arr,int startIndex,int endIndex) {
if(startIndex >= endIndex) { //递归结束条件
return;
}
int pivotIndex =partition(arr,startIndex,endIndex); //得到基准元素的位置
quickSort(arr,startIndex,pivotIndex-1); //根据基准元素分成两部分进行递归排序
quickSort(arr,pivotIndex+1,endIndex);
}
private static int partition(int[] arr, int startIndex, int endIndex) {
int pivot = arr[startIndex]; //取第一个位置的元素作为基准元素(也可以选择随机位置)
int left = startIndex;
int right = endIndex;
while(left != right) {
while(left < right && arr[right] > pivot) {
right--;
}
while(left < right && arr[left] <= pivot) {
left++;
}
if(left < right) {
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
}
//pivot和指针重合点交换
arr[startIndex] = arr[left];
arr[left] = pivot;
return left;
}
1.4 (NC-88) 寻找第K大
快速排序
有一个整数数组,请你根据快速排序的思路,找出数组中第K大的数。
给定一个整数数组a,同时给定它的大小n和要找的K(1<=K<=n),请返回第K大的数(包括重复的元素,不用去重),保证答案存在。
输入:[1,3,5,2,2],5,3
返回值:2
public int findKth(int[] a, int n, int K) {
return findK(a, 0,n-1,K);
}
private int findK(int[] arr, int left, int right,int K) {
if(left <= right) {
int pivot = partition(arr, left,right);
if(pivot == K-1) {
return arr[pivot];
}else if(pivot < K - 1) {
return findK(arr, pivot+1, right, K);
}else {
return findK(arr, left, pivot-1, K);
}
}
return -1;
}
private int partition(int[] arr, int left, int right) {
int pivot = arr[left];
while (left < right) {
while (left < right && arr[right] <= pivot) {
right--;
}
arr[left] = arr[right];
while (left < right && arr[left] >= pivot) {
left++;
}
arr[right] = arr[left];
}
arr[left] = pivot;
return left;
}
public static void main(String[] args) {
FindKth f = new FindKth();
int arr[] = new int[] {1,3,5,2,2};
int res = f.findKth(arr, 5, 3);
System.out.print(res);
}