请描述快速排序和归并排序的思路 (京东)
快速排序实现可分为以下几步:
- 在数组中选一个基准数(通常为数组第一个)
- 将数组中小于基准数的数据移到基准数左边,大于基准数的移到右边
- 对于基准数左、右两边的数组,不断重复以上两个过程,直到每个子集只有一个元素,即为全部有序
时间复杂度为:
public void quickSort(int[] nums,int begin,int end){
if (begin >= end) return;
int temp = nums[begin];
int i = begin,j = end;
while (i != j){
while (nums[j] >= temp && i != j) j--;
if (i != j) {
nums[i] = nums[j];
i++;
}
while (nums[i] <= temp && i != j) i++;
if (i != j){
nums[j] = nums[i];
j--;
}
}
nums[i] = temp;
quickSort(nums,begin,i-1);
quickSort(nums,i+1,end);
}
归并排序
是一类与插入排序、交换排序、选择排序不同的另一种排序方法。归并的含义是将两个或两个以上的有序表合并成一个新的有序表。归并排序有多路归并排序、两路归并排序 , 可用于内排序,也可以用于外排序。这里仅对内排序的两路归并方法进行讨论。
一、两路归并排序算法思路
分而治之(divide - conquer);每个递归过程涉及三个步骤
第一, 分解: 把待排序的 n 个元素的序列分解成两个子序列, 每个子序列包括 n/2 个元素.
第二, 治理: 对每个子序列分别调用归并排序MergeSort, 进行递归操作
第三, 合并: 合并两个排好序的子序列,生成排序结果.
时间复杂度
public static void mergeSort(int[] data) {
sort(data, 0, data.length - 1);
}
public static void sort(int[] data, int left, int right) {
if (left >= right)
return;
// 找出中间索引
int center = (left + right) / 2;
// 对左边数组进行递归
sort(data, left, center);
// 对右边数组进行递归
sort(data, center + 1, right);
// 合并
merge(data, left, center, right);
print(data);
}
/**
* 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序
* @param data 数组对象
* @param left 左数组的第一个元素的索引
* @param center 左数组的最后一个元素的索引,center+1是右数组第一个元素的索引
* @param right 右数组最后一个元素的索引
*/
public static void merge(int[] data, int left, int center, int right) {
// 临时数组
int[] tmpArr = new int[data.length];
// 右数组第一个元素索引
int mid = center + 1;
// third 记录临时数组的索引
int third = left;
// 缓存左数组第一个元素的索引
int tmp = left;
while (left <= center && mid <= right) {
// 从两个数组中取出最小的放入临时数组
if (data[left] <= data[mid]) {
tmpArr[third++] = data[left++];
} else {
tmpArr[third++] = data[mid++];
}
}
// 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)
while (mid <= right) {
tmpArr[third++] = data[mid++];
}
while (left <= center) {
tmpArr[third++] = data[left++];
}
// 将临时数组中的内容拷贝回原数组中
// (原left-right范围的内容被复制回原数组)
while (tmp <= right) {
data[tmp] = tmpArr[tmp++];
}
}
public static void print(int[] data) {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + "\t");
}
System.out.println();
}
给定一个数组,返回第二大的那个数。(滴滴)
不要采用先排序再取出第二大元素,因为最快的排序算法也要,本解法时间复杂度为
思路:
1)假设数组中的前两个元素就是最大和第二大,即max和smax;
2)从数组的第二个元素开始遍历数组,当有元素大于max的时候,将max赋值给smax,再将最大的那个元素赋值给max;
3)如果当前元素小于max,并且大于smax,那么就让smax当前元素。
这样max和smax中存储的一直都是当前已经扫描过的所有元素的第一大和第二大了。
public int findTheSecond(int[] nums){
int max,smax;
if (nums[0]>nums[1]) {
max=nums[0];
smax=nums[1];
} else {
max=nums[1];
smax=nums[0];
};
for (int i=2;i<nums.length;i++){
if (nums[i] > max) {
smax = max;
max = nums[i];
}
if (nums[i] < max && nums[i] > smax) smax = nums[i];
}
return smax;
}
给定一个链表,判断链表中是否有环。 (滴滴)
boolean isCircle(ListNode head){
if (head == null) return false;
ListNode slow = head, quick = head;
while (quick.next != null){
quick = quick.next;
if (quick.next != null) {
quick = quick.next;
}else {
return false;
}
slow = slow.next;
if (quick == slow) return true;
}
return false;
}
接雨水 (字节 小红书)
int trap(int[] height){
int sum = 0;
int[] max_left = new int[height.length];
int[] max_right = new int[height.length];
for (int i = 1; i < height.length - 1; i++) {
max_left[i] = Math.max(max_left[i - 1], height[i - 1]);
}
for (int i = height.length - 2; i >= 0; i--) {
max_right[i] = Math.max(max_right[i + 1], height[i + 1]);
}
for (int i = 1; i < height.length - 1; i++) {
int min = Math.min(max_left[i], max_right[i]);
if (min > height[i]) {
sum = sum + (min - height[i]);
}
}
return sum;
}
给你链表的头结点 head
,请将其按 升序 排列并返回 排序后的链表 。(字节)
要求
- 你可以在
O(n log n)
时间复杂度和常数级空间复杂度下,对链表进行排序吗?