在做一道剑指Offer的题的时候,有道题涉及到快排的思路,一开始就很快根据以前的思路写出了代码,但似乎有些细节不太对劲,自己拿数据试了下果然。然后折腾了下并记录下一些小坑,还有总结下划分方法partition的两种思路。

partition思路1——交换思路

以待排序数组的第一个元素为基准值key,然后两个指针i和j,先从后面开始找(这个是个坑后面会总结)第一个比基准key小的数字,停下来,然后再从前面开始找第一个比基准key大的数字,停下来。

然后交换这两个指针的元素。

当两个指针相碰的时候,也就是i>=j了,或者说就i==j了(因为按照代码应该不存在i>j的情况),就再把a[begin]的值和a[i]或者a[j]的值交换就好了。

大概代码如下:

int key = a[begin];
int i = begin, j = end;//坑三i的值 while(i < j) {
//坑2顺序
//while(i < j && a[i] <= key)i++;//从左边开始找到第一个比key大的数字然后停下来
while(i < j && a[j] >= key)j--;//从右边开始找到第一个比key小的数字然后停下来
while(i < j && a[i] <= key)i++;//从左边开始找到第一个比key大的数字然后停下来 swap(a, i, j);
} swap(a, begin, i);//最后是i还是j的位置和begin交换都行,因为最后是i==j

partition思路2——填坑思路

想下我们的swap一般是怎么实现的: 

int temp = a[i];
a[i] = a[j];
a[j] = temp;

一开始把i位置的值存在一个地方,这相当于i位置有个坑了,因为其他数字可以覆盖i位置了。

填坑思路的写法就是根据这个思想。

一样是以待排序数组的第一个元素为基准,我们将a[begin]的值存在key的位置,然后begin就有个坑了吧。

然后两个指针,i从begin开始,j从end开始,一开始从后面也就是j从后往前遍历,找到第一个比key小的数字,然后把这个数字覆盖在i的位置上,因为i是从begin开始,而begin位置有个坑是可以填的。

好了j的东西既然写在了i的位置,意味着j的位置也是个坑了,这个时候i就开始往前遍历,找到第一个比key大的值,然后填在j的坑处,一直以此类推。

最后,i和j指针相碰后,只要把key的值填到最后i或者说是j的位置就好了。

看个大概代码:

int key = a[begin];//begin的位置被存了起来,这个时候可以利用begin的位置存其他值了

        int i = begin, j = end;

        while(i < j) {
while(i < j && a[j] >= key) j--;
//一开始进来i就是begin,本来begin的值已经在key那里相当于begin或者说是i这里有个坑所以可以覆盖
a[i] = a[j]; while(i < j && a[i] <= key) i++;
//a[j]的值刚刚已经放在之前的i位置了,相当于j的位置有坑可以覆盖
a[j] = a[i];
} a[i] = key;//最后填i或j都行了,因为最后一次肯定是i==j了

关于写快排代码过程中遇到的小坑

坑1:

这个其实也不是坑拉,就递归流程中对结束条件的理解。一开始我就想着begin==end就结束,如果是begin>end就出错了。

但其实递归过程中是肯定会出现begin>end的情况的,因为partition+1和partition-1这一步。

所以正确的递归终结条件应该直接是begin >= end。    (提醒一下归并排序中的终结条件是begin == end)

坑2:

坑2就是到底是先从后面往前找还是先从前面往后面找。

如果是填坑的思路就不会陷入这个坑中,因为你要先填a[i]或者说是a[begin]的坑,那么肯定是先从后往前遍历。

但如果是交换的思路就emmm我就踩辽。

因为如果你是先从前往后找,那么出去第一个循环的条件一定是找到第一个比key大的数字了。(不可能是因为i >=j,因为外层循环确保了i < j才进来);

但这个时候第二个循环可能因为i>=j而出去,那么这个时候,如果指针相碰了,i和j一起指向一个比key大的值,然后和a[begin]交换的话,就会出现一个比key大的值出现在key的左边,就错辽。

所以应该要先从后往前遍历这样就会找到第一个比key小的值就出去循环,第二个循环即使i=j了出去也不怕,因为这个时候和begin交换是没有问题的。

补充:这个while(i < j && a[i] >= key)中的i<j也是少不了的,不然的话有越界的危险

坑3:

坑3是i是从哪里开始的,这个在填坑思路中也不会出错,因为第一个坑是在begin那里嘛所以肯定i是从begin开始……

然后我用交换思路的时候又采坑了,就想着反正是比较begin后面的数字嘛,就直接i从begin+1开始。

这样会有什么问题呢?

考虑只有三个数字:11,32,41;这其实已经是有序的了,那么i如果从begin+1开始,那么i会直接原地跳出循环,因为32是第一个比key11大的数字嘛;

然后j从右边开始遍历,也会停在32的位置,因为虽然42,32都比key大讲道理应该继续往下遍历的,但我们条件中还有i < j这一项,所以就停下来了。

然后出去循环,和begin交换——就变成32,11,41的错误答案了。

所以 i 要从begin开始噢。

补充小坑:

“交换思路”中,其实还有个小问题,就是条件那里应该只能够a[i]<=key,而不能是<。

因为如果是小于,那么第一次i就会原地停下,然后和a[j]交换。然后最后key归位我们是和begin和i交换的方式,但这个时候begin已经不是key了,就会出错。

实际上,如果改写成了a[i]<key的话,这个时候和“填坑思路”的效果就是一样的了。

Java实现快排+小坑+partition的两种思路的更多相关文章

  1. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  2. Java去除掉HTML里面所有标签的两种方法——开源jar包和自己写正则表达式

    Java去除掉HTML里面所有标签,主要就两种,要么用开源的jar处理,要么就自己写正则表达式.自己写的话,可能处理不全一些自定义的标签.企业应用基本都是能找开源就找开源,实在不行才自己写…… 1,开 ...

  3. 19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  4. java 的对象拷贝(有深浅拷贝两种方式,深拷贝实现的两种方式(逐层实现cloneable接口,序列化的方式来实现))

    Java提高篇--对象克隆(复制)(转自:http://www.cnblogs.com/Qian123/p/5710533.html#_label0)   阅读目录 为什么要克隆? 如何实现克隆 浅克 ...

  5. (转载)Eclipse将引用了第三方jar包的Java项目打包成可执行jar的两种方法

    转载自:http://www.cnblogs.com/lanxuezaipiao/p/3291641.html 方案一:用Eclipse自带的Export功能 步骤1:准备主清单文件 "MA ...

  6. Java中x+=y和x=x+y两种实现的区别

    先看下边两段代码,各有什么错? 例一: short s1 = 1; s1 = s1 + 1; 例二: short s1 = 1; s1 += 1; 第一段代码无法通过编译,由于 s1+1 在运算时会自 ...

  7. 数据结构队列的java实现,包括线性和链式两种方式

    实现的思路为: 采用泛型的方式,首先定义了一个Queue的接口,然后通过实现该接口实现了线性和链式的两种形式的队列: 接口代码如下: package com.peter.java.dsa.interf ...

  8. 极光推送经验之谈-Java后台服务器实现极光推送的两种实现方式

    原创作品,可以转载,但是请标注出处地址http://www.cnblogs.com/V1haoge/p/6439313.html Java后台实现极光推送有两种方式,一种是使用极光推送官方提供的推送请 ...

  9. java的取出map里所有元素的两种方式

    /* * 取出map元素的两种方式 */package com.map.test; import java.util.HashMap;import java.util.Iterator;import ...

随机推荐

  1. 麻省理工《C内存管理和C++面向对象编程》笔记---第一讲:认识C和内存管理

    最近一年都在用.net和Java,现在需要用C了.昨天看到博客园首页的麻省理工开放课程,就找来看看,正好复习一下.这门<C内存管理和C++面向对象编程>不是那种上来就变量,循环的千篇一律的 ...

  2. VMware Workstation虚拟机安装Windows 7系统

    1.进入VMware Workstation虚拟机软件界面,选择新建虚拟机

  3. openStack kvm 虚拟机CPU颗粒化控制

    前一篇理解cpu topology对CPU Topology进行了学习总结,这里想总结下OpenStack下vCPU与pCPU常用的的绑定方式. 在尝试这些绑定之前,尤其是处理NUMA架构时还是建议看 ...

  4. MySql集合查询

    SELECT语句的查询结果是元组的集合,所以多个SELECT语句的结果可进行集合操作. 集合操作主要包括并操作UNION.交操作INTERSECT.差操作EXCEPT. 注意,参加集合操作的各查询结果 ...

  5. 10、Perl5中19个最重要的文件系统工具

    转载:http://www.cnblogs.com/nkwy2012/p/6027157.html 在写脚本处理文件系统时,经常需要加载很多模块.其中好多有用函数分散在各种不同的模块中.它们有些是Pe ...

  6. 记录一次坎坷的linux内网渗透过程瞎折腾的坑

    版权声明:本文为博主的原创文章,未经博主同意不得转载. 写在前面 每个人都有自己的思路和技巧,以前遇到一些linux的环境.这次找来一个站点来进行内网,写下自己的想法 目标环境 1.linux  2. ...

  7. HTML5学习笔记(七)HTML5 服务器发送事件(Server-Sent Events)

    Server-Sent 事件指的是网页自动获取来自服务器的更新. 以前也可能做到这一点,前提是网页不得不询问是否有可用的更新.通过服务器发送事件,更新能够自动到达. EventSource 对象用于接 ...

  8. cf835(预处理 + 记忆化dp)

    题目链接: http://codeforces.com/contest/835/problem/D 题意: 定义 k 度回文串为左半部分和右半部分为 k - 1 度的回文串 . 给出一个字符串 s, ...

  9. 2017-10-4 清北刷题冲刺班a.m

    P101zhx a [问题描述]你是能看到第一题的 friends 呢.——hjaHja 拥有一套时光穿梭技术,能把字符串以超越光速的速度传播,但是唯一的问题是可能会 GG.在传输的过程中,可能有四种 ...

  10. Mysql-15-mysql分布式应用

    1.分布式应用的概念和优势 分布式数据库是指利用高速网络将物理上分散的多个数据存储单元连接起来组成一个逻辑上统一的数据库.分布式数据库的基本思想是将原来集中式数据库中的数据分散存储到多个通过网络连接的 ...