递归和分治算法(2)--合并排序和快速排序

news2024/11/23 11:02:58

目录

一、合并排序相关题

1、合并排序

2、逆序对

二、快速排序相关题

1、快速排序

目录

一、合并排序相关题

1、合并排序

2、逆序对

二、快速排序相关题

1、快速排序

2、中位数选取

三、循环赛日程表


一、合并排序相关题

1、合并排序

       合并排序的原理:

(1)如果左值等于右值,返回左值对应的数组值

(2)取mid=(left+right)/2,对左侧部分合并排序,右侧部分合并排序

(3)对左排和右排进行合并排序,由于进入总合并排序中,不一定是左排和右排你一个我一个或者左排全部小于右排的原理。所以遵循几个原则:两者都没读到末尾时,数组值较小的先进总排,若有一者读到末尾,那么另一者将所有的值按顺序进入总排。

        合并排序代码:

//合并排序
public class mergesort {
    public static void main(String[] args)
    {
        int[] nums=new int[]{9,8,5,2,4,3,1,6};
        int[] nums_sort=MergeSort(nums,0,nums.length-1);
        for(int i:nums_sort)
        {    System.out.print(i);
             System.out.print(" ");
        }
    }
    public static int [] MergeSort(int a[],int left,int right)
    {
        if(left==right)
            return new int[]{a[left]};
        int mid=(left+right)/2;
        int leftarr[]=MergeSort(a,left,mid);                     //左侧合并排序
        int rightarr[]=MergeSort(a, mid+1,right);                //右侧合并排序

        int newarr[]=new int[leftarr.length+rightarr.length];    //左排与右排合并
        
        int m=0;int i=0;int j=0;
        while(i<leftarr.length&&j<rightarr.length)               //在两个哨兵分别没有走完左排和右排时,选择较小的先进入newarr
            newarr[m++]=leftarr[i]<rightarr[j]?leftarr[i++]:rightarr[j++];
        while(i<leftarr.length)                                  //如果右排哨兵到末尾,那么左排的剩余部分加入newarr 
            newarr[m++]=leftarr[i++];
        while(j<rightarr.length)                                 //如果左排哨兵到末尾,那么右排的剩余部分加入newarr
            newarr[m++]=rightarr[j++];
        return newarr;
            
    }
    
}

2、逆序对

        逆序对就是数组中前面的一个数大于后面的一个数,这两个数组成的关系就是逆序对。比如{4,2,3,1}中{4,2},{4,3},{4,1},{2,1},{3,1}都是逆序对。如果使用暴力枚举方法就是O(n^2)的复杂度,我们可以使用归并排序法降低复杂度到O(nlogn)

        利用归并排序,每次以左侧排序数组为基准。

(1)若左哨兵小于右哨兵,则左哨兵加1,继续判定(1)

(2)若左哨兵大于右哨兵,逆序对加mid-左哨兵索引+1,右哨兵加1,返回(1)。

(3)若右哨兵到末尾则算法停止。

逆序对代码:

public class 逆序对 {
    public static void main(String[] args)
    {
        int[] nums=new int[]{9,8,5,2,4,3,1,6};
        int nums_sort=MergeSort(nums,0,nums.length-1);
        System.out.println(nums_sort);
        
    }
    public static int MergeSort(int a[], int left, int right) 
    {
    if (left >= right)
        return 0;
    int mid = (left + right) / 2;
    int leftans = MergeSort(a, left, mid);
    int rightans = MergeSort(a, mid + 1, right);

    int p = left; int q = mid + 1; int count = 0;
    int[] temp = new int[right - left + 1]; int index = 0;

    while (p <= mid && q <= right) {
        if (a[p] > a[q]) {
            count += mid - p + 1; 
            temp[index++] = a[q++];
        } else {
            temp[index++] = a[p++];
        }
    }
    while (p <= mid) 
        temp[index++] = a[p++];    
    while (q <= right) 
        temp[index++] = a[q++];   
    for (int i = 0; i < index; i++) 
        a[left + i] = temp[i];    
    return count + leftans + rightans;
    }
}

二、快速排序相关题

1、快速排序

        快速排序复杂度为O(nlogn),具体原理请参见下面博客,写的很好。

        另外如果想从O(nlogn)降低到O(n)的话,就需要随机划分法,随机选择一个基准点,而不是永远用第一个。

        快速排序有两种做法,一种是两个哨兵移动,另一种是使用基准元素遍历整个数组分割为两个部分。

快速排序(java实现)_快速排序java-CSDN博客

快速排序代码:

import java.util.Scanner;
public class QuickSort {
    public static void main(String[] args){
        int []arr=new int[999];int j =0;
        String input=new Scanner(System.in).nextLine();
        String[] numbers=input.split("\\s+");

        for(String number : numbers)
        {
            arr[j++]=Integer.parseInt(number);
        }
        quickSort(arr, 0, j-1);
        for (int i = 0; i < j; i++) {
            System.out.print(arr[i]);
            System.out.print(" ");
        }
    }

    public static void quickSort(int[] arr,int low,int high){
        int i=low; int j=high; int temp,t;
        if(low>high){
            return;
        }
        //temp就是基准位
        temp = arr[low];

        while (i<j) {           
            while (temp<=arr[j--]&&i<j) ;       //右哨兵减一
            while (temp>=arr[i++]&&i<j) ;       //左哨兵加一
            if (i<j) {                          //交换
                t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }
        }       
         arr[low] = arr[i];        //将基准为与i和j相等位置的数字交换
         arr[i] = temp;
        quickSort(arr, low, j-1);  //递归调用左半数组
        quickSort(arr, j+1, high); //递归调用右半数组
    }
}

2、中位数选取

        依赖于快速排序进行中位数选取。

(1)确定数组长度的奇偶性,确定中位数的位置,奇数直接调select函数找第mid小的元素,偶数则返回第mid和mid-1的平均值。

(2)select函数根据快速排序思想,调用partition函数将数组分为两部分,并找到第k个小的元素。

(3)partition函数就是一个快速排序对特定位置的数组进行快排。

public class MedianFinder {
    public static void main(String[] args){
        int num[]={1,2,3,4,5,6};
        double med=medianFinder(num);
        System.out.println(med);
    }
    //寻找数组中的中位数
    public static double medianFinder(int[] num){
        int mid;
        int len=num.length;
        double med;

        if(len%2==0){
            mid=len/2;
         med=(select(num,mid)+select(num,mid-1))/2.0;
        }
        else{
            mid=(len-1)/2;
            med=(double)select(num,mid);
        }
        return med;
    }
    //找到第k个小的元素
    public static int select(int[] num,int k){
        int low=0;
        int high=num.length-1;
        int j=0;
        int val=0;
        while(low<high){
            j=partition(num,low,high);
            if(j==k){
                val=num[j];
                break;
            }else if(j>k){
                high=j-1;
            }else{
                low=j+1;
            }
        }
        return val;

    }
    //快速排序
    public static int partition(int[] num,int low,int high){
        int i=low;
        int j=high;
        int temp=num[i];
        while(i<j){
            while(i<j && temp<=num[j--]);           
            if(i<j)
                num[i++]=num[j];
            while(i<j && temp>=num[i++]);
            if(i<j)
                num[j--]=num[i];
        }
        num[i]=temp;
        return i;
    }

        快速排序的复杂度是依据数组的对称性,如果每次选择pivot都是中位数,那么时间复杂度就是 O(nlogn),但如果每次选的数都是一侧第一个值那么时间复杂度就会是O(n^2),所以为了保证不对称分布也可以保证相对对称的效果,那么就需要在选择pivot上更加随机。

        可以通过修改partition函数让每一步选择pivot都是当前数组中的一个随机值,并调换他和左值的位置,产生一个随机划分效果。

    //随机划分快速排序  
    public static int RandomizedPartition(int []num,int low,int high)
    {
        int i=new Random().nextInt(low,high);
        swap(num,i,low);
        return partition(num, low, high);
    }

    public static void swap(int []num,int i,int low)
    {
        int tmp=num[i];
        num[i]=num[low];
        num[low]=tmp;
    }

三、循环赛日程表

        设(n=2^k)n个选手参加循环赛,每个选手必须与其他n-1个选手各赛一次,每个选手一天只能比赛一次,循坏赛一共n-1天。可以参照下图,能看到每一个2^k的格子都是对角对称的,所以可以依照这个原理进行分治。

        原理:

(1)分治构造左上和右上的日程表。

(2)将左上的日程表复制到右下,右上的日程表复制到左下。

        伪代码如下:

public static void table(int arr[][],int i,int j,int n)
{
    if(n>1)
    {
        table(arr,i,j,n/2);                //构造左上的日程表
        table(arr,i+n/2,j,n/2);            //构造右上的日程表
        copy(arr,i,j,i+n/2,j+n/2,n/2);     //左上的日程表复制到右下
        copy(arr,i+n/2,j,i,j+n/2,n/2);     //右上的日程表复制到左下
    }
    else
        return arr[i];
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1069504.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文读懂|zRAM 内存压缩机制

内存是计算机系统最重要的资源之一&#xff0c;当操作系统内存不足时&#xff0c;进程申请内存将会失败&#xff0c;从而导致其运行异常或者崩溃。 Linux 内核提供 swap 机制来解决内存不足的情况&#xff0c;其原理是&#xff1a; 当系统内存不足时&#xff0c;内核会将进程不…

线性数据—栈、队列、链表

一、栈 Stack&#xff08;存取O(1)&#xff09; 先进后出&#xff0c;进去123&#xff0c;出来321。 基于数组&#xff1a;最后一位为栈尾&#xff0c;用于取操作。 基于链表&#xff1a;第一位为栈尾&#xff0c;用于取操作。 1.1、数组栈 /*** 基于数组实现的顺序栈&#…

记录:Unity脚本的编写2.0

目录 前言控制方法键盘控制鼠标控制虚拟控制器控制 平移和旋转 前言 前面记录了一些简单的unity脚本用来控制unity中对象模型的移动&#xff08;或者不能叫控制&#xff0c;毕竟它是开启之后自己在跑的&#xff09;&#xff0c;那么让模型可以根据用户的操作来进行变化的方法自…

SQL监控工具

什么是 SQL 监控 SQL 监视是跟踪和分析整个 MSSQL 生态系统的过程&#xff0c;以识别性能问题并防止依赖数据库的应用程序变慢和/或遇到中断&#xff0c;它有助于获取有关 SQL 服务器的数据库会话、查询、作业、CPU 和内存资源、群集、配置和可用性组的信息。 为什么 MSSQL 监…

JavaScript 笔记: 函数

1 函数声明 2 函数表达式 2.1 函数表达式作为property的value 3 箭头函数 4 构造函数创建函数&#xff08;不推荐&#xff09; 5 function 与object 5.1 typeof 5.2 object的操作也适用于function 5.3 区别于⼀般object的⼀个核⼼特征 6 回调函数 callback 7 利用function的pr…

TensorFlow入门(十、共享变量)

使用tf.Variable方法创建变量 使用tf.Variable方法创建变量时有两点需要注意: ①一般情况下,使用tf.Variable方法创建的变量都有作用域,也可叫做变量的可用性范围,即在变量所属的模型内,变量的名字是有效可用的。 ②使用tf.Variable方法创建变量时,会生成一个新的变量。如果在一…

激活函数与loss的梯度

激活函数&#xff1a; 最开始由生物学家对青蛙的神经元机制进行研究发现&#xff0c;青蛙的神经元有多个输入x0、x1、x2&#xff0c;响应值是他们加权后的结果&#xff0c;但响应值如果小于阈值&#xff0c;则不会响应&#xff0c;而只有大于阈值时&#xff0c;才会有固定的响应…

LeetCode 1277. 统计全为 1 的正方形子矩阵【动态规划】1613

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

紫光 DDR3 IP核调试

1. IP核简介 直接编写DDR2/DDR3的逻辑工作量不太大&#xff0c;但是涉及到双边延、高速率的读取&#xff0c;时序很复杂。因此一般抖采用IP核实现。紫光的SDRAM IP核为HMIC_H IP。 HMIC_H IP 包括了 DDR Controller、DDR PHY 和 PLL&#xff0c;用户通过 AXI4 接口实现数据的读…

十三、MySQL 主从复制

一、MySQL 主从复制 1. 主从复制原理 主库 有一个 工作线程 I/O dump thread&#xff08;转储线程&#xff09;。从库 有两个工作线程 I/O thread 和 SQL thread。主库 通过 I/O dump thread 给 从库 I/O thread 传送 binlog 日志。 主从同步过程中&#xff1a; 主库 把接收的…

大厂笔试真题【栈】美团2023春招-火车迷【欧弟算法】全网最全大厂秋招题解

文章目录 题目描述与示例题目描述输入描述输出描述示例一输入输出 示例二输入输出 解题思路代码pythonJavaC时空复杂度 华为OD算法/大厂面试高频题算法练习冲刺训练 题目描述与示例 题目描述 小美是一个火车迷。最近她在观察家附近火车站的火车驶入和驶出情况&#xff0c;发现…

C/C++学习 -- Base64算法

Base64算法概述 Base64是一种用于将二进制数据表示为ASCII字符集中的可打印字符的编码方法。它由64个不同的字符组成&#xff0c;通常包括26个大写字母、26个小写字母、10个数字以及两个特殊字符&#xff08;通常是""和"/"&#xff09;。Base64编码的目的…

【Vue基础-数字大屏】地图标记涟漪效果设置

一、需求说明 将地图中北京市、陕西市、南宁市分别以实心圆、涟漪圆、涟漪圆标记出来 二、代码实践 涉及三个文件 1、App.vue: <template><Screen1/> </template> <script> import Screen1 from "./components/Screen1.vue"; export def…

如何应对数据安全四大挑战?亚马逊云科技打出“组合拳”

数字经济时代&#xff0c;数据被公认为继土地、劳动力、资本、 技术之后的又一重要生产要素。对于企业而言&#xff0c;数据则是一切创新与关键决策的根源。 然而&#xff0c;企业在发挥数据资产的商业价值方面&#xff0c;却面临诸多挑战&#xff0c;比如敏感数据识别、跨组织…

【高阶数据结构】图详解第一篇:图的基本概念及其存储结构(邻接矩阵和邻接表)

文章目录 1. 图的基本概念1.1 什么是图1.2 有向图和无向图1.3 完全图1.4 邻接顶点1.5 顶点的度1.6 路径1.7 路径长度1.8 简单路径与回路1.9 子图1.10 连通图1.11 强连通图1.12 生成树 2. 图的存储结构2.1 邻接矩阵2.2 邻接矩阵代码实现结构定义构造函数添加边打印图测试 2.3 邻…

leetCode 718.最长重复子数组 动态规划 + 优化(滚动数组)

718. 最长重复子数组 - 力扣&#xff08;LeetCode&#xff09; 给两个整数数组 nums1 和 nums2 &#xff0c;返回 两个数组中 公共的 、长度最长的子数组的长度 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,3,2,1], nums2 [3,2,1,4,7] 输出&#xff1a;3 解释&…

2023年台州市第三届网络安全技能大赛(MISC)这是神马

这是神马 考点&#xff1a;冰蝎流量特征数据包&#xff0c;需要解密 emoj解密 冰蝎之前做过 特征就是先base64编码在AES编码 我们在数据包里面找到了密钥&#xff1a;144a6b2296333602 这里我们知道了密钥我们就去解密 先筛选HTTP协议 导出HTTP数据流可以看到传了shell.php 随…

美容美甲小程序商城的作用是什么

美容院往往有很高需求&#xff0c;女性悦己经济崛起&#xff0c;加之爱美化程度提升&#xff0c;无论线下环境还是线上互联网信息冲击&#xff0c;美容服务、化妆产品等市场规格一直稳增不减。 通过【雨科】平台制作美容美甲商城&#xff0c;售卖相关服务/产品&#xff0c;模块…

记两次内网入侵溯源

1.1、入侵告警 1、某天深夜主机防护突然爆出CS木马后门&#xff0c;这攻击队不讲武德呀&#xff0c;还好没睡着2、赶紧叫醒旁边看流量设备的哥们儿&#xff0c;尝试Shiro 反序列漏洞攻击成功3、测试目标网站存在shiro反序列化漏洞1.2、上机排查 1、上机将CS木马下载下来&…

Vue3 reactive和ref详解

reactive Vue3.0中的reactive reactive 是 Vue3 中提供的实现响应式数据的方法。在 Vue2 中响应式数据是通过 defineProperty 来实现的&#xff0c;在 Vue3 中响应式数据是通过 ES6 的 Proxy来实现的。reactive 参数必须是对象 (json / arr)如果给 reactive 传递了其它对象 默…