深入浅出排序算法之快速排序(重要)⭐⭐⭐⭐⭐

news2024/11/17 19:37:59

目录

1. 算法介绍⭐⭐⭐⭐⭐

1.1 图示解析

2. 执行流程和代码实现

2.1 挖坑法⭐⭐⭐⭐

2.2 Hoare法⭐⭐⭐⭐

2.3 前后指针法(了解即可)

2.4  非递归实现快速排序(了解即可)

4. 性能分析

5. 算法改进

5.1 三数选中法

5.2 最后几行数据采用直接插入排序


1. 算法介绍⭐⭐⭐⭐⭐

快速排序也是“交换”类的排序,运用了分治的思想,它通过多次划分操作实现排序。以升序为例,其执行流程可以概括为:每一趟选择当前所有子序列中的一个关键字(通常是第一个)作为基准值,将子序列中比基准值小的移动到基准值的前边,比基准值大的移到基准值后边;当本趟所有子序列都被基准值以上述规则划分完毕后,会得到新的一组更短的子序列,它们成为下一趟划分的初始序列集。

(1)从待排序区间选择一个数,作为基准值(mid);

(2)Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边;

(3)采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序,或者小区间的长度 == 0,代表没有数据。

1.1 图示解析

2. 执行流程和代码实现

2.1 挖坑法⭐⭐⭐⭐

小伙伴们,自行放大图片看一下,很细滴!!!

    /*
    下面性能部分细讲
    时间复杂度:
        最好情况是:O(NlogN) 均匀分割待排序列,尽量满二叉树
        最坏情况是:O(N^2),有序或者逆序,单分支的树
    空间复杂度:
        最好情况是:O(logN) 树的高度,把持着mid变量
        最坏情况是:O(N),有序或者逆序,单分支的树
    稳定性:不稳定
     */
    //这里为了统一其他排序算法的接口,所以只传一个参数——数组
    public static void quickSort(int[] array) {

        quick(array, 0, array.length - 1);
    }

    //划分
    private static void quick(int[] array, int start, int end) {
        //这里为什么是>=,因为防止end到了start后面,导致越届
/*
        s e
        mid
    例如:1 2
        再递归进去,end == -1,start == 0,所以必须交 =
 */
        if (start >= end) {
            return;
        }

        int mid = partition1(array, start, end);
        //递归左边
        quick(array, start, mid - 1);
        //递归右边
        quick(array, mid + 1, end);
    }

    //第一种:快速排序,挖坑法,无优化版本
    //每一趟的操作,交换,填坑
    private static int partition1(int[] array, int left, int right) {
        int tmp = array[left];//保存最左位置的值
        //开始找坑
        //多次左右走
        while (left < right) {
            //先右往左走,找一个值比tmp小的,一次循环或者说一次向右走
            // 这里必须要是 >= ,不然当下标0和下标array.length的话,就会陷入死循环,这两个下标的值一直在交换
            //提问:为什么不需要再次++,去进入循环呢?
            while (left < right && array[right] >= tmp)
                right--;
            array[left] = array[right];
            //从左往右走,找一个值比tmp大的,一次循环或者说一次向左走
            while (left < right && array[left] <= tmp)
                left++;
            array[right] = array[left];
        }
        array[left] = tmp;
        return left;
    }

    public static void main(String[] args) {
        int[] a = {10,10,9,8,7,6,5,4,3,2,1};
        Sort.quickSort2(a);
        for (int x : a) {
            System.out.print(x + " ");
        }
    }

2.2 Hoare法⭐⭐⭐⭐

思路:设置两个下标指针i和j,j向左走,找到比基准值(pivot)小的值先停下来,不交换,i向右走,找到比基准值(pivot)大的值先停下来,然后交换array[i] 和 array[j],当i和j相遇后,交换array[i] 和 array[left]。

    /*
    下面性能部分细讲
    时间复杂度:
        最好情况是:O(NlogN) 均匀分割待排序列,尽量满二叉树
        最坏情况是:O(N^2),有序或者逆序,单分支的树
    空间复杂度:
        最好情况是:O(logN) 树的高度,把持着mid变量
        最坏情况是:O(N),有序或者逆序,单分支的树
    稳定性:不稳定
     */
    //这里为了统一其他排序算法的接口,所以只传一个参数——数组
    public static void quickSort(int[] array) {

        quick(array, 0, array.length - 1);
    }

    //划分
    private static void quick(int[] array, int start, int end) {
        //这里为什么是>=,因为防止end到了start后面,导致越届
/*
        s e
        mid
    例如:1 2
        再递归进去,end == -1,start == 0,所以必须交 =
 */
        if (start >= end) {
            return;
        }

        int mid = partition2(array, start, end);
        //递归左边
        quick(array, start, mid - 1);
        //递归右边
        quick(array, mid + 1, end);
    }

    //第二种快速排序,Hoare法,主要是交换的方式不同
    private static int partition2(int[] array, int left, int right) {
        int tmp = array[left];
        int i = left;//把left开始的下标保存起来先
        while (left < right) {
/*
为什么不能先走左呢?
答:因为先走左的话,最后right会停在比mid大的值的下标,left找到right停下来,交换i和left的值,导致不满足快排的结果
 */
            //这两个循环,永远是right追left
            while (left < right && array[right] >= tmp)
                right--;
            while (left < right && array[left] <= tmp)
                left++;
            swap(array, left, right);//自定义的交换方法
        }
        swap(array, i, left);
        return left;
    }

    public static void main(String[] args) {
        int[] a = {10,10,9,8,7,6,5,4,3,2,1};
        Sort.quickSort2(a);
        for (int x : a) {
            System.out.print(x + " ");
        }
    }


2.3 前后指针法(了解即可)

总体思路:prev一直往后走(prev++),当array[prev]小于array[left]的时候,cur++。当array[cur] != array[prev]时。

具体看如下的过程:

    //第三种排序算法:前后指针法
    //写法一
    private static int partition3(int[] array, int left, int right) {
        int cur = left;//prev记录的是最后一位比array[left]的下标
        int prev = left + 1;
        while (cur <= right) {
            if (array[prev] < array[left] && array[++cur] != array[prev]) {
                swap(array, prev, cur);
            }
            prev++;
        }
        swap(array, left, cur);
        return cur;
    }

第一步:先把第一个元素定为基准值。

cur 记录第一个元素6的下标,prev记录第二个元素的1的下标。

第二步:因为第一步的1 < 6,cur++且没发生交换,prev++(注意:prev无论如何都会一直往前走) 。

第三步:因为第二步的2 < 6,cur++且没发生交换,prev++(注意:prev无论如何都会一直往前走) 。

第四步:因为第三步的7 > 6,所以 cur不往前走,prev++,来到元素9的下标。

第五步:因为第四步的9 > 6,所以 cur不往前走,prev++,来到元素3的下标。

第六步:因为第五步的3 < 6,所以cur++来到元素7的下标,接着交换array[cur]和array[prev]的元素,后prev++(代码有体现)。

第七步:因为第五步的4 < 6,所以cur++来到元素的下标,接着交换array[cur]和array[prev]的元素,后prev++(代码有体现)。

第八步:因为第七步的5 < 6,所以cur++来到元素的下标,接着交换array[cur]和array[prev]的元素,后prev++(代码有体现)。

第九步:因为第八步的10 > 6,所以 cur不往前走,prev++,来到元素8的下标。

然后排序元素6左边子序列和右边子序列,一直重复,直至排序完成!

总结:我们可以发现cur永远都是记录着小于基准值的最后一个元素的下标,不断地把比基准值大的元素往右推! 

        /*
    下面性能部分细讲
    时间复杂度:
        最好情况是:O(NlogN) 均匀分割待排序列,尽量满二叉树
        最坏情况是:O(N^2),有序或者逆序,单分支的树
    空间复杂度:
        最好情况是:O(logN) 树的高度,把持着mid变量
        最坏情况是:O(N),有序或者逆序,单分支的树
    稳定性:不稳定
     */
    //这里为了统一其他排序算法的接口,所以只传一个参数——数组
    public static void quickSort(int[] array) {

        quick(array, 0, array.length - 1);
    }

    //划分
    private static void quick(int[] array, int start, int end) {
        //这里为什么是>=,因为防止end到了start后面,导致越届
/*
        s e
        mid
    例如:1 2
        再递归进去,end == -1,start == 0,所以必须交 =
 */
        if (start >= end) {
            return;
        }

        int mid = partition3(array, start, end);
        //递归左边
        quick(array, start, mid - 1);
        //递归右边
        quick(array, mid + 1, end);
    }

    //第三种排序算法:前后指针法
    //写法一
    private static int partition3(int[] array, int left, int right) {
        int cur = left;//prev记录的是最后一位比array[left]的下标
        int prev = left + 1;
        while (cur <= right) {
            if (array[prev] < array[left] && array[++cur] != array[prev]) {
                swap(array, prev, cur);
            }
            prev++;
        }
        swap(array, left, cur);
        return cur;
    }

    public static void main(String[] args) {
        int[] a = {6,1,2,7,9,3,4,5,10,8};
        Sort.quickSort2(a);
        for (int x : a) {
            System.out.print(x + " ");
        }
    }

2.4  非递归实现快速排序(了解即可)

使用栈完成效果

(1)先准备一个栈,先调用一次partition1()方法,把6放到最终位置。

 (2)将左边子序列的start和end下标进栈,右子序列的start和end下标进栈。

(3)然后出栈,先赋值个right,再给left,循环进行,就能完成排序。

注意:会将右子序列排序完成,再去排序左子序列,小伙伴们可以根据我们上面的部分流程,把整个算法流程画出来!

  
    //第一种:快速排序,挖坑法,无优化版本
    //每一趟的操作,交换,填坑
    private static int partition1(int[] array, int left, int right) {
        int tmp = array[left];//保存最左位置的值
        //开始找坑
        //多次左右走
        while (left < right) {
            //先右往左走,找一个值比tmp小的,一次循环或者说一次向右走
            // 这里必须要是 >= ,不然当下标0和下标array.length的话,就会陷入死循环,这两个下标的值一直在交换
            //提问:为什么不需要再次++,去进入循环呢?
            while (left < right && array[right] >= tmp)
                right--;
            array[left] = array[right];
            //从左往右走,找一个值比tmp大的,一次循环或者说一次向左走
            while (left < right && array[left] <= tmp)
                left++;
            array[right] = array[left];
        }
        array[left] = tmp;
        return left;
    }

    //非递归实现快速排序
    //用栈实现
    public static void quickSort2(int[] array){
        Deque<Integer> stack = new LinkedList<>();
        int left = 0;
        int right = array.length - 1;
        int mid = partition1(array,left,right);
        if(mid > left + 1){
            stack.push(left);
            stack.push(mid - 1);
        }
        if(mid < right - 1){
            stack.push(mid + 1);
            stack.push(right);
        }
        while(!stack.isEmpty()){
            right = stack.pop();
            left = stack.pop();
            mid = partition1(array,left,right);
            if(mid > left + 1){
                stack.push(left);
                stack.push(mid - 1);
            }
            if(mid < right - 1) {
                stack.push(mid + 1);
                stack.push(right);
            }
        }
    }

4. 性能分析

(1)最好的情况:快速排序最理想的情况就是满二叉树, 此时时间复杂度是:O(nlogn);空间复杂度是:O(logn)。

(2)最坏的情况:原数据有序或者逆序,这样快速排序就成了单分支树了,此时时间复杂度是:O(n^2),空间复杂度是:O(n)。

5. 算法改进

5.1 三数选中法

取待排序数组中 头、中、尾三个位置的元素、取中间值作为基准元素。(此种方式最好,递归调用栈的深度最低)1.取待排序数组中 头、中、尾三个位置的元素、取中间值作为基准元素。(此种方式最好,递归调用栈的深度最低)。

    //优化的方案,均匀的分割!三数取中法
    private static int midThree(int[] array, int left, int right) {
        int mid = (left + right) / 2;
        if(array[left] > array[right]){
            if(array[mid] < array[right]){
                return right;
            }else if(array[mid] > array[left]){
                return left;
            }else {
                return mid;
            }
        }else{
            //array[right] > array[left]
            if(array[mid] < array[left]){
                return left;
            }else if(array[mid] > array[right]){
                return right;
            }else{
                return mid;
            }
        }
    }

改进后的代码是:

    /*
    时间复杂度:
        最好情况是:O(NlogN) 均匀分割待排序列,尽量满二叉树
        最坏情况是:O(N^2),有序或者逆序,单分支的树
    空间复杂度:
        最好情况是:O(logN) 树的高度,把持着mid变量
        最坏情况是:O(N),有序或者逆序,单分支的树
    稳定性:不稳定
     */
    //这里为了统一其他排序算法的接口,所以只传一个参数——数组
    public static void quickSort(int[] array) {

        quick(array, 0, array.length - 1);
    }

    //划分
    private static void quick(int[] array, int start, int end) {
        //这里为什么是>=,因为防止end到了start后面,导致越届
/*
        s e
        mid
    例如:1 2
        再递归进去,end == -1,start == 0,所以必须交 =
 */
        if (start >= end) {
            return;
        }

        //优化一:
        //排序,返回到最终位置的元素下标
        int index = midThree(array,start,end);
        swap(array,start,index);
        int mid = partition3(array, start, end);
        //递归左边
        quick(array, start, mid - 1);
        //递归右边
        quick(array, mid + 1, end);
    }

    //第一种:快速排序,挖坑法,无优化版本
    //每一趟的操作,交换,填坑
    private static int partition1(int[] array, int left, int right) {
        int tmp = array[left];//保存最左位置的值
        //开始找坑
        //多次左右走
        while (left < right) {
            //先右往左走,找一个值比tmp小的,一次循环或者说一次向右走
            // 这里必须要是 >= ,不然当下标0和下标array.length的话,就会陷入死循环,这两个下标的值一直在交换
            //提问:为什么不需要再次++,去进入循环呢?
            while (left < right && array[right] >= tmp)
                right--;
            array[left] = array[right];
            //从左往右走,找一个值比tmp大的,一次循环或者说一次向左走
            while (left < right && array[left] <= tmp)
                left++;
            array[right] = array[left];
        }
        array[left] = tmp;
        return left;
    }

    //优化的方案,均匀的分割!三数取中法
    private static int midThree(int[] array, int left, int right) {
        int mid = (left + right) / 2;
        if(array[left] > array[right]){
            if(array[mid] < array[right]){
                return right;
            }else if(array[mid] > array[left]){
                return left;
            }else {
                return mid;
            }
        }else{
            //array[right] > array[left]
            if(array[mid] < array[left]){
                return left;
            }else if(array[mid] > array[right]){
                return right;
            }else{
                return mid;
            }
        }
    }

5.2 最后几行数据采用直接插入排序

直接插入排序:数据越有序,效率越高!

    /*
    时间复杂度:
        最好情况是:O(NlogN) 均匀分割待排序列,尽量满二叉树
        最坏情况是:O(N^2),有序或者逆序,单分支的树
    空间复杂度:
        最好情况是:O(logN) 树的高度,把持着mid变量
        最坏情况是:O(N),有序或者逆序,单分支的树
    稳定性:不稳定
     */
    //这里为了统一其他排序算法的接口,所以只传一个参数——数组
    public static void quickSort(int[] array) {

        quick(array, 0, array.length - 1);
    }

    //划分
    private static void quick(int[] array, int start, int end) {
        //这里为什么是>=,因为防止end到了start后面,导致越届
/*
        s e
        mid
    例如:1 2
        再递归进去,end == -1,start == 0,所以必须交 =
 */
        if (start >= end) {
            return;
        }

        //优化二:
        if(end - start + 1 <= 14){
            insertSort2(array,start,end);
        }
        //优化一:
        //排序,返回到最终位置的元素下标
        int index = midThree(array,start,end);
        swap(array,start,index);
        int mid = partition3(array, start, end);
        //递归左边
        quick(array, start, mid - 1);
        //递归右边
        quick(array, mid + 1, end);
    }

    //第一种:快速排序,挖坑法,无优化版本
    //每一趟的操作,交换,填坑
    private static int partition1(int[] array, int left, int right) {
        int tmp = array[left];//保存最左位置的值
        //开始找坑
        //多次左右走
        while (left < right) {
            //先右往左走,找一个值比tmp小的,一次循环或者说一次向右走
            // 这里必须要是 >= ,不然当下标0和下标array.length的话,就会陷入死循环,这两个下标的值一直在交换
            //提问:为什么不需要再次++,去进入循环呢?
            while (left < right && array[right] >= tmp)
                right--;
            array[left] = array[right];
            //从左往右走,找一个值比tmp大的,一次循环或者说一次向左走
            while (left < right && array[left] <= tmp)
                left++;
            array[right] = array[left];
        }
        array[left] = tmp;
        return left;
    }

    //直接插入排序
    public static void insertSort2(int[] arr,int left,int right) {
        //代码可以从i = 1开始算起,但是做题画图时,一定要从i = 0开始算起
        for (int i = left + 1; i < arr.length; i++) {
            int j = i - 1;
            int tmp = arr[i];
            for (; j >= left; j--) {
                //如果arr[j] > tmp变成arr[j] >= tmp就变成不稳定了
                if (arr[j] > tmp) {
                    arr[j + 1] = arr[j];
                } else {
                    break;
                }
            }
            arr[j + 1] = tmp;
        }
    }

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

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

相关文章

ChatGPT正在测试原生文件分析功能,DALL·E 3能P图啦!

10月29日&#xff0c;有部分用户在社交平台上分享&#xff0c;ChatGPT Plus正在测试原生文件上传、分析功能&#xff0c;可以通过文本问答的方式&#xff0c;对上传的PDF等数据文件进行提问、搜索。 例如&#xff0c;上传一份50页的员工手册PDF文件&#xff0c;然后向ChatGPT提…

基于Qt Designer 操作教程

​本章将简介使用 Qt Creator 里自带的 Qt Designer,使用 Qt Designer 比较方便的构造 UI 界面。特点是方便布局,比较形象。 ## 使用 UI 设计器开发程序 在这小节里我们继续学习如何使用 Qt Designer 开发程序,Qt Designer 是属于 Qt Creator 的一个功能而已,大家不要搞混…

一款针对SpringBootEnv页面进行快速漏洞利用

参考GitHub - 0x727/SpringBootExploit: 项目是根据LandGrey/SpringBootVulExploit清单编写&#xff0c;目的hvv期间快速利用漏洞、降低漏洞利用门槛。 &#x1f4dd; TODO 支持Eureka XStream deserialization RCE支持Fastjson 内存马注入支持更多可以使用JNDI内存马注入反序…

英语——歌曲篇——My Heart Will Go On——我心永恒

My Heart Will Go On [Celine Dion我心永恒] Every night in my dreams I see you,I feel you, That is how I know you go on Far across the distance And spaces between us You have come to show you go on Near,far,wherever you are I believe that the heart does g…

huggingface离线模式及默认保存路径

T5Tokenizer.from_pretrained()函数会在线下载huggingface上的模型&#xff0c;并自动保存在C盘的.cache路径下&#xff1a; C:\Users\xxxxx\.cache\huggingface\hub\models--google--t5-v1_1-base # 这里xxxxx代表自己电脑用户名huggingface离线下载 以google/t5-v1_1-base为…

RabbitMQ入门到实战教程,消息队列实战,改造配置MQ

RabbitMQ入门到实战教程&#xff0c;MQ消息中间件&#xff0c;消息队列实战-CSDN博客 3.7.Topic交换机 3.7.1.说明 Topic类型的Exchange与Direct相比&#xff0c;都是可以根据RoutingKey把消息路由到不同的队列。 只不过Topic类型Exchange可以让队列在绑定BindingKey 的时候…

吊打98%的JAVA同行,这份阿里P8架构师升级手册登上天梯!

前言&#xff1a; 我们都是IT人&#xff0c;所以&#xff0c;我们注定了很像。 前段时间有个朋友去阿里面试&#xff0c;作为一个社招生&#xff0c;太多痛苦了。都知道进大厂最好的时机就是应届生的时候。作为社招生&#xff0c;太难了。 我这位朋友经历了五轮面试最后才上…

【AIFEM案例操作】水泵强度分析

AIFEM是由天洑自主研发的一款通用的智能结构仿真软件&#xff0c;助力用户解决固体结构相关的静力学、动力学、振动、热力学等实际工程问题&#xff0c;软件提供高效的前后处理工具和高精度的有限元求解器&#xff0c;帮助用户快速、深入地评估结构的力学性能&#xff0c;加速产…

reactor (百万并发服务器) -- 1

为了从点滴开始&#xff0c;文章会先从一些基础socket去补充一些经常发生但是没有很深入去思考的细节。然后我们再开始去设计reactor的设计&#xff0c;可以选择跳过起过前面部分。 为了能从0开始去设计&#xff0c;测试&#xff0c;优化...整个过程会分为2-3篇文章输出&#x…

【NI-DAQmx入门】传感器基础知识

1.什么是传感器&#xff1f; 传感器可将真实的现象&#xff08;例如温度或压力&#xff09;转换为可测量的电流和电压&#xff0c;因而对于数据采集应用必不可少。接下来我们将介绍您所需的测量类型及其对应的传感器类型。在开始之前&#xff0c;您还可以先了解一些传感器术语&…

优化python程序执行速度

1、问题背景 最近使用python编写的一个蓝牙应用程序&#xff0c;在使用过程中发现传输大量数据时会产生丢包&#xff0c;导致无法接收到完整的数据包。蓝牙接收程序的代码如下。 #蓝牙数据接收处理线程def bt_recv_thread(self):recv_time time.time()while(self.thread_run)…

BUUCTF 神秘龙卷风 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 神秘龙卷风转转转&#xff0c;科学家用四位数字为它命名&#xff0c;但是发现解密后居然是一串外星人代码&#xff01;&#xff01;好可怕&#xff01; 密文&#xff1a; 下载附件&#xff0c;解压得到一个.rar压缩…

动态蛇形卷积管状结构分割【CVPR 2023】

论文下载地址&#xff1a;Excellent-Paper-For-Daily-Reading/nn-block at main 类别&#xff1a;模块 时间&#xff1a;2023/10/31 摘要 血管和道路等管状结构在各种临床和自然环境中具有极其重要的意义&#xff0c;在这些环境中&#xff0c;精确分割对于下游任务的准确性…

C++ Set

定义 set不同于vector,strin,list这种存储容器&#xff0c;set是一种关联式容器&#xff0c;底层是搜二叉&#xff1b; 功能 set可以确定唯一的值&#xff0c;可以排序去重。 接口 insert() #include <iostream> #include<set> using namespace std;int main…

机泵设备如何通过设备健康管理平台实施预测性维护

机泵设备在工业生产中起着至关重要的作用&#xff0c;但长时间运行和频繁使用容易引发各种故障。为了提高机泵设备的可靠性和效率&#xff0c;预测性维护成为一种重要的管理策略。设备健康管理平台作为一种先进的工具&#xff0c;为机泵设备的预测性维护提供了有力支持。本文将…

第七届山东省黄炎培职业教育创新创业大赛圆满结束

山东省黄炎培职业教育创新创业大赛作为职教领域的一项品牌赛事&#xff0c;自举办以来&#xff0c;参赛院校覆盖面不断扩大&#xff0c;大赛水平和社会影响力不断提高&#xff0c;已成为全省职业教育领域的品牌赛事&#xff0c;是激发创新创业活力的重要抓手和有效载体&#xf…

UIAlertController 修改 title 或 message 样式相关

UIAlertController 文字换行后默认对齐方式为居中,若想调整其相关样式属性可以借鉴如下方式进行修改,具体实现方式 code 如下: NSString *msg "1、注销≠退出登录;\n注销:对不再使用的账号进行清空移除;注销后,App中数据将全部丢失,不可再找回;\n2、注销后,与账号相关的…

【Linux】配置JDKTomcat开发环境及MySQL安装和后端项目部署

目录 一、jdk安装配置 1. 传入资源 2. 解压 3. 配置 二、Tomcat安装 1. 解压开启 2. 开放端口 三、MySQL安装 1. 解压安装 2. 登入配置 四、后端部署 1. 数据库 2. 导入.war包 3. 修改端口 4.开启访问 一、jdk安装配置 打开虚拟机 Centos 登入账号&#xff…

控梦术(一)之什么是清明梦

控梦术 首先&#xff0c;问大家一个问题。在梦中&#xff0c;你知道自己是在做梦吗&#xff1f;科学数据表明&#xff0c;大约23%的人在过去一个月中&#xff0c;至少有一次在梦中意识到自己正在做梦。科学家把这叫做清醒梦或者叫做清明梦。科学家说&#xff0c;每个人都能学会…

【C++ 系列文章 -- 程序员考试 下午场 C++ 专题 201711 】

文章目录 1.1 C 题目六1.1.1 填空&#xff08;1&#xff09;详解1.1.2 填空&#xff08;2&#xff09;详解1.1.2.1 C this 的使用 1.1.3 填空&#xff08;3&#xff09;详解1.1.4 填空&#xff08;4&#xff09;详解1.1.5 填空&#xff08;5&#xff09;详解1.1.6 填空&#xf…