1.从第一个元素开始,该元素可以认为已经被排序
2.取下一个元素tem,从已排序的元素序列从后往前扫描
3.如果该元素大于tem,则将该元素移到下一位
4.重复步骤3,直到找到已排序元素中小于等于tem的元素
5.tem插入到该元素的后面,如果已排序所有元素都大于tem,则将tem插入到下标为0的位置
6.重复步骤2~5
归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法,归并排序对序列的元素进行逐层折半分组,然后从最小分组开始比较排序,合并成一个大的分组,逐层进行,最终所有的元素都是有序的
归并排序的核心:分治
//基础的二路归并
//核心思想:划分为两个子问题
//左边处理一下,得到左边的信息
//右边处理一下,得到右边的信息
//最后再处理一下,横跨左右两边的信息
void merge_sort(int *arr, int l, int r){
if(l >= r) return;
int mid = (l + r) / 2;
cout << endl;
cout << "sort : " << l << "<--->" << r << " : " << endl;
for(int i = l; i <= r; i++){
cout << arr[i] << " ";
}
cout << endl; //换行
//上面几行用来打印 方便观察
merge_sort(arr, l, mid);
merge_sort(mid + 1, r);
vector temp(r - l + 1);
int k = 0, p1 = l, p2 = mid + 1;
//当左右两个区间还有元素的时候
while(p1 <= mid || p2 <= r){
//1. 右区间为空
//2. 左区间没空,并且,左区间的元素比较小
if((p2 > r) || (p1 <= mid && arr[p1] <= arr[p2])){
temp[k] = arr[p1];//最开始是把p1指向元素放进temp 0下标中
k++, p1++;
}else{//左区间空了,把右区间元素放进去
temp[k] = arr[p2];
k++, p2++;
}
}//右区间还有元素的话,继续把右区间的元素放进去
for(int i = l; i <= r; i++){
arr[i] = temp[i - l];
}//上面那两行相当于覆盖操作
//打印
for(int i = l; i <= r; i++){
cout << arr[i] << " ";
}
cout << arr[i] << " ";
return ;
}
int main(){
int a[10] = {1, 9, 0, 2, 5, 6, 2, 7, 1, 9};
merge_sort(a, 0, 9);
for(int i = 0; i < 10; i++){
cout << a[i] << " ";
}
return 0;
}
//归并排序稳定 时间复杂度:O(nlogn)
//空间复杂度:那个临时数组是在函数内部开辟的空间,属于栈上开辟的变量,先开辟n/2后再释放n/2,再开辟n/2,再释放... 最大的情况是开辟n的,所以空间复杂度为 O(n)
问题:电脑内存大小2GB,如何对一个40GB的文件进行排序?
//插入排序:
/*
* 插入排序算法:
* 1、以数组的某一位作为分隔位,比如index=1,假设左面的都是有序的.
*
* 2、将index位的数据拿出来,放到临时变量里,这时index位置就空出来了.
*
* 3、从leftindex=index-1开始将左面的数据与当前index位的数据(即temp)进行比较,如果array[leftindex]>temp,
* 则将array[leftindex]后移一位,即array[leftindex+1]=array[leftindex],此时leftindex就空出来了.
*
* 4、再用index-2(即leftindex=leftindex-1)位的数据和temp比,重复步骤3,
* 直到找到<=temp的数据或者比到了最左面(说明temp最小),停止比较,将temp放在当前空的位置上.
*
* 5、index向后挪1,即index=index+1,temp=array[index],重复步骤2-4,直到index=array.length,排序结束,
* 此时数组中的数据即为从小到大的顺序.
*
*/
public class InsertSort {
private int[] array;
private int length;
public InsertSort(int[] array){
this.array = array;
this.length = array.length;
}
public void display(){
for(int a: array){
System.out.print(a+" ");
}
System.out.println();
}
/*
* 插入排序方法
*/
public void doInsertSort(){
for(int index = 1; index<length; index++){//外层向右的index,即作为比较对象的数据的index
int temp = array[index];//用作比较的数据
int leftindex = index-1;
while(leftindex>=0 && array[leftindex]>temp){//当比到最左边或者遇到比temp小的数据时,结束循环
array[leftindex+1] = array[leftindex];
leftindex--;
}
array[leftindex+1] = temp;//把temp放到空位上
}
}
public static void main(String[] args){
int[] array = {38,65,97,76,13,27,49};
InsertSort is = new InsertSort(array);
System.out.println("排序前的数据为:");
is.display();
is.doInsertSort();
System.out.println("排序后的数据为:");
is.display();
}
}
//归并排序:
public class MergeSort {
//两路归并算法,两个排好序的子序列合并为一个子序列
public void merge(int[] a,int left,int mid,int right){
int[] tmp=new int[a.length];//辅助数组
int p1=left,p2=mid+1,k=left;//p1、p2是检测指针,k是存放指针
while(p1<=mid && p2<=right){
if(a[p1]<=a[p2])
tmp[k++]=a[p1++];
else
tmp[k++]=a[p2++];
}
while(p1<=mid) tmp[k++]=a[p1++];//如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
while(p2<=right) tmp[k++]=a[p2++];//同上
//复制回原数组
for (int i = left; i <=right; i++)
a[i]=tmp[i];
}
public void mergeSort(int[] a,int start,int end){
if(start<end){//当子序列中只有一个元素时结束递归
int mid=(start+end)/2;//划分子序列
mergeSort(a, start, mid);//对左侧子序列进行递归排序
mergeSort(a, mid+1, end);//对右侧子序列进行递归排序
merge(a, start, mid, end);//合并
}
}
@Test
public void test(){
int[] a = { 49, 38, 65, 97, 76, 13, 27, 50 };
mergeSort(a, 0, a.length-1);
System.out.println("排好序的数组:");
for (int e : a)
System.out.print(e+" ");
}
}
//力扣题:21 88 56
//21
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1 == null){
return list2;
}else if(list2 == null){
return list1;
} else if (list1.val < list2.val) {
list1.next = mergeTwoLists(list1.next, list2);
return list1;
} else {
list2.next = mergeTwoLists(list1, list2.next);
return list2;
}
}
//虚拟头节点+迭代方法
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode hair = new ListNode(-1);
ListNode pre = hair;
while(list1 != null && list2 != null){
if(list1.val <= list2.val){
pre.next = list1;
list1 = list1.next;
}else{
pre.next = list2;
list2 = list2.next;
}
//继续往后迭代
pre = pre.next;
}
// 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
pre.next = list1 == null ? list2 : list1;
return hair.next;
}
}
/*复杂度分析
时间复杂度:O(n + m),其中 n 和 m 分别为两个链表的长度。因为每次调用递归都会去掉 l1 或者 l2 的头节点(直到至少有一个链表为空),函数 mergeTwoList 至多只会递归调用每个节点一次。因此,时间复杂度取决于合并后的链表长度,即 O(n+m)。
空间复杂度:O(n + m),其中 n 和 m 分别为两个链表的长度。递归调用 mergeTwoLists 函数时需要消耗栈空间,栈空间的大小取决于递归调用的深度。结束递归调用时 mergeTwoLists 函数最多调用 n+m 次,因此空间复杂度为 O(n+m)
*/
/*Java中arraycopy方法
System.arraycopy(src, srcPos, dest, destPos, length);
src表示源数组
srcPos表示源数组中拷贝元素的起始位置。
dest表示目标数组
destPos表示拷贝到目标数组的起始位置
length表示拷贝元素的个数*/
//需要注意的是在进行数组拷贝时,目标数组必须有足够的空间来存放拷贝的元素,否则就会发生角标越界异常。
// !!!!另外还需要注意的是目标数组相对应位置上的元素会被覆盖掉
//88 合并两个有序数组
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int p1 = m-1;
int p2 = n-1;
int p = m+n-1;
while((p1>=0) && (p2>=0))
nums1[p--] = (nums1[p1]<nums2[p2]) ? nums2[p2--] : nums1[p1--];
System.arraycopy(nums2,0,nums1,0,p2+1);
}
}
//时间复杂度:O(m+n)。
//指针移动单调递减,最多移动 m+n 次,因此时间复杂度为 O(m+n)
//空间复杂度:O(1)
//直接对数组nums1原地修改,不需要额外空间
//56 合并区间
//以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间
class Solution {
//思路:区间只有三种情况:包含、相交、独立 我们合并前两种
//对区间起始位置从小到大排序 A[] B[] A[尾] >= B[头] 就合并
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, new Comparator<int[]>() {//排序
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
int[][] res = new int[intervals.length][2];//结果集数组
int ind = -1;//索引 告诉我们合并的集合应该放在结果集的哪个位置
for(int[] interval : intervals) {
//说明是我们第一个拿到的区间 或者 当前数组头部大于上次拿到数组的尾部
if(ind == -1 || interval[0] > res[ind][1]) {
res[++ind] = interval;
}else {//此时相交或者包含 确定边界
res[ind][1] = Math.max(res[ind][1], interval[1]);
}
}//走到这里后面要切割无用部分
return Arrays.copyOf(res, ind + 1);
}
}
/**
* 剑指offer51.数组中的逆序对(困难)
* @author: William
* @time:2022-05-09
*/
public class Num51 {
public int reversePairs(int[] nums) {
if(nums.length < 2) return 0;
return merge_sort(nums, 0, nums.length - 1);
}
private int merge_sort(int[] nums, int L, int R) {
if(L >= R) return 0;
int mid = L + ((R - L) >> 1), ans = 0;
//分治 两个数组都是递增的,p1都比p2大,那p1后面的数更加比p2大
ans = merge_sort(nums, L, mid) + merge_sort(nums, mid + 1, R);
//归并
int[] tmp = new int[R - L + 1];
int k = 0, p1 = L, p2 = mid + 1;
while(p1 <= mid || p2 <= R) {
if((p2 > R) || (p1 <= mid && nums[p1] <= nums[p2])) {
tmp[k++] = nums[p1++];
}else {
//只有p1 > p2 的情况下才走到这 是逆序对
tmp[k++] = nums[p2++];
ans += (mid - p1 + 1);
}
}//将数组元素放到原数组中
for(int i = 0; i < tmp.length; i++) nums[i + L] = tmp[i];
return ans;
}
//k神版本
int[] nums, temp;
public int reversePairs1(int[] nums) {
this.nums = nums;
temp = new int[nums.length];
return mergeSort(0, nums.length - 1);
}
private int mergeSort(int L, int R) {
//终止条件
if(L >= R) return 0;
//递归划分
int m = (L + R) >> 1;
int res = mergeSort(L, m) + mergeSort(m + L, R);
//合并阶段
int i = L, j = m + 1;
for(int k = L; k <= R; k++) {
temp[k] = nums[k];
}
for(int k = L; k <= R; k++) {
if(i == m + 1)
nums[k] = temp[j++];
else if(j == R + 1 || temp[i] <= temp[j])
nums[k] = temp[i++];
else {
nums[k] = temp[j++];
res += m - i + 1;//统计逆序对
}
}
return res;
}
}
/**
* 合并K个升序链表(困难)
* @author: William
* @time:2022-05-09
*/
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public class Num23 {
//小顶堆
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) return null;
PriorityQueue<ListNode> q = new PriorityQueue<ListNode>(new Comparator<ListNode>() {
@Override
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val;
}
});//把链表中的数据塞到小顶堆中
for(ListNode list : lists) if(list != null) q.offer(list);
//新的链表来存储合并后的结果集 并且从虚拟头节点开始
ListNode ret = new ListNode(-1), p = ret;
while(!q.isEmpty()) {
ListNode cur = q.poll();
p.next = cur;//继续往后迭代
p = cur;
if(cur.next != null) q.offer(cur.next);
}
return ret.next;
}
}
/**
* 排序链表
* @author: William
* @time:2022-05-09
*/
public class Num148 {
public ListNode sortList(ListNode head) {
int n = 0;
ListNode p = head;
while(p != null) {
p = p.next;
n++;
}//得到链表的长度
return merge_sort(head, n);
}
private ListNode merge_sort(ListNode head, int n) {
if(n <= 1) return head;
int l_cnt = n >> 1, r_cnt = n - l_cnt;
ListNode ret = new ListNode(-1), L = head, R = L, p = L;
for(int i = 0; i < l_cnt - 1; i++) p = p.next;//p此时走到左边链表的尾部
R = p.next;
p.next = null;//此时完成左右链表的拆分
//开始合并
L = merge_sort(L, l_cnt);
R = merge_sort(R, r_cnt);
p = ret;
while(L != null || R != null) {
if((R == null) || (L != null && L.val <= R.val)) {
p.next = L;
p = L;
L = L.next;
}else {
p.next = R;
p = R;
R = R.next;
}
}
return ret.next;
}
}
/**
* 两根搜索树中的所有元素
* @author: William
* @time:2022-05-10
*/
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
public class Num1305 {
//二叉搜索树在进行中序遍历的时候是递增的
public List<Integer> getAllElements(TreeNode root1, TreeNode root2){
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
List<Integer> res = new ArrayList<>();
//得到两个递增集合
inorder(root1, list1);
inorder(root2, list2);
int L = 0, R = 0;
while(L < list1.size() || R < list2.size()) {
if( (R >= list2.size()) || (L < list1.size() && list1.get(L) <= list2.get(R) )){
res.add(list1.get(L++));
}else {
res.add(list2.get(R++));
}
}
return res;
}
public void inorder(TreeNode root, List<Integer> list) {
if(root == null) return;
inorder(root.left, list);
list.add(root.val);
inorder(root.right, list);
}
//直接调用集合工具类哈哈哈
List<Integer> ans;
public List<Integer> getAllElements1(TreeNode root1, TreeNode root2) {
ans = new ArrayList<>();
dfs(root1);
dfs(root2);
Collections.sort(ans);
return ans;
}
void dfs(TreeNode root) {
if (root == null) return;
dfs(root.left);
ans.add(root.val);
dfs(root.right);
}
}
/**
* 区间和的个数(困难)
* @author: William
* @time:2022-05-11
*/
public class Num327 {
//通过前缀和求区间和
//low <= sum[j] - sum[i] <= upper
int lower, upper;
public int countRangeSum(int[] nums, int lower, int upper) {
//初始化
this.lower = lower;
this.upper = upper;
long[] sum = new long[nums.length + 1];
sum[0] = 0;//求前缀和
for(int i = 0; i < nums.length; i++) sum[i + 1] = sum[i] + nums[i];
return merge_sort(sum, 0, sum.length - 1);
}
private int merge_sort(long[] nums, int L, int R) {
if(L >= R) return 0;
int mid = L + ((R - L) >> 1);
int ans = 0;
ans += merge_sort(nums, L, mid);
ans += merge_sort(nums, mid + 1, R);
ans += countTwoPart(nums, L, mid, mid + 1, R, lower, upper);
int k = 0, p1 = L, p2 = mid + 1;
long[] tmp = new long[R - L + 1];
while(p1 <= mid || p2 <= R) {
if((p2 > R) || (p1 <= mid && nums[p1] <= nums[p2])) {
tmp[k++] = nums[p1++];
}else {
tmp[k++] = nums[p2++];
}
}
for(int i = 0; i < tmp.length; i++) nums[i + L] = tmp[i];
return ans;
}
//在并的过程中看有多少个元素符合条件
private int countTwoPart(long[] nums, int l1, int r1, int l2, int r2, int lower, int upper) {
int ans = 0;//记录多少个区间符合状态
//j是右侧区间固定数 左侧查找范围
for(int j = l2, k1 = l1, k2 = l1; j <= r2; j++) {
//lower <= j-i <= upper -> j - lower i >= i >= j - upper
long a = nums[j] - upper;
long b = nums[j] - lower;//确定两个边界
while(k1 <= r1 && nums[k1] < a) k1++;//找到第一个边界就停
//k2找比较大的边界 大于等于b的话说明站在最后一个的后面 等于也不要停 往后站一位
while(k2 <= r1 && nums[k2] <= b) k2++;
ans += k2 - k1;
}
return ans;
}
}
/**
* 计算右侧小于当前元素的个数(困难)
* @author: William
* @time:2022-05-11
*/
public class Num315 {
class Data{//每一个data的cnt记录右侧有多少元素小于当前元素
int ind, val, cnt;
public Data(int ind, int val) {
this.ind = ind;
this.val = val;
this.cnt = 0;
}
}
public List<Integer> countSmaller(int[] nums) {
Data[] data = new Data[nums.length];
for(int i = 0; i < nums.length; i ++) {
data[i] = new Data(i, nums[i]);//把集合数据塞进去
}
merge_sort(data, 0, data.length - 1);
Arrays.sort(data, new Comparator<Data>() {//下标从小到大排序
@Override
public int compare(Data o1, Data o2) {
return o1.ind - o2.ind;
}
});
List<Integer> res = new ArrayList<>();
for(Data datum : data) {
res.add(datum.cnt);//加入到结果集中
}
return res;
}
private void merge_sort(Data[] data, int L, int R) {
if(L >= R) return;
int mid = (L + R) >> 1;
merge_sort(data, L, mid);
merge_sort(data, mid + 1, R);
//合并过程
int k = 0, p1 = L, p2 = mid + 1;
Data[] tmp = new Data[R - L + 1];
while(p1 <= mid || p2 <= R) {//两边任意一个有值就可以
if((p2 > R) || (p1 <= mid && data[p1].val > data[p2].val)) {
//在前面找到一个比后面大的元素 开始计数
data[p1].cnt += (R - p2 + 1);
tmp[k++] = data[p1++];
}else {//右侧小于左侧的情况
tmp[k++] = data[p2++];
}
}
for(int i = 0; i < tmp.length; i++) {
data[i + L] = tmp[i];//将tmp数组中数据覆盖到data中
}
}
}
/**
* 首个共同祖先
* @author: William
* @time:2022-05-11
*/
public class Num0408 {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return null;
if(root == p || root == q) return root;//至少找到一个
TreeNode L = lowestCommonAncestor(root.left, p, q);
TreeNode R = lowestCommonAncestor(root.right, p, q);
if(L != null && R != null) return root;//p q都找到
if(L != null && R == null) return L;//左边是找到的
return R;
}
}
/**
* 层数最深叶子节点的和
* @author: William
* @time:2022-05-11
*/
public class Num1302 {
int ans, max_k;
public int deepestLeavesSum(TreeNode root) {
ans = 0;
max_k = 0;
getAns(root, 0);
return ans;
}
private void getAns(TreeNode root, int k) {
if(root == null) return;
if(k == max_k) ans += root.val;//当前叶子节点到了最深层
else if(k > max_k) {//达到新的最深层,前面的作废
max_k = k;
ans = root.val;
}//继续向下递归
getAns(root.left, k + 1);
getAns(root.right, k + 1);
}
}