扑克牌k4、1o、7、9、k从小到k排烈

\ 经验分享
简易扑克牌游戏-Java基础练习(面向对象+集合)
List 接口的实现类 ArrayList
SortedSet (Set 接口子类,实现了排序)接口的实现类 TreeSet
SortedMap (Map 接口的子类,实现了 key 排序)接口的实现类 TreeMap
Comparable 接口及其 comparaTo() 方法的实现
重写 equals() 和 hashCode()
静态代码块
Const.java
public class Const {
static Map&String, Integer& pokerC
static Map&String, Integer& pokerC
pokerContent = new HashMap&&();
pokerColor = new HashMap&&();
pokerContent.put("2", 2);
pokerContent.put("3", 3);
pokerContent.put("4", 4);
pokerContent.put("5", 5);
pokerContent.put("6", 6);
pokerContent.put("7", 7);
pokerContent.put("8", 8);
pokerContent.put("9", 9);
pokerContent.put("10", 10);
pokerContent.put("J", 11);
pokerContent.put("Q", 12);
pokerContent.put("K", 13);
pokerContent.put("A", 14);
pokerColor.put("黑桃", 4);
pokerColor.put("红桃", 3);
pokerColor.put("方片", 2);
pokerColor.put("梅花", 1);
public static int getPokerContentInteger(String content){
return pokerContent.get(content);
public static int getPokerColorInteger(String color){
return pokerColor.get(color);
Poker.java
public class Poker implements Comparable {
public Poker() {
public String getColor() {
public void setColor(String color) {
this.color =
public String getContent() {
public void setContent(String content) {
this.content =
public String toString() {
return getColor() + getContent();
public int compareTo(Object o) {
Poker poker = (Poker)
int thisPokerContentInteger = Const.getPokerContentInteger(this.getContent());
int pokerContentInteger = Const.getPokerContentInteger(poker.getContent());
if (thisPokerContentInteger & pokerContentInteger) {
} else if (thisPokerContentInteger == pokerContentInteger) {
int thisPokerColorInteger = Const.getPokerColorInteger(this.getColor());
int pokerColorInteger = Const.getPokerColorInteger(poker.getColor());
if (thisPokerColorInteger & pokerColorInteger) {
} else if (thisPokerColorInteger == pokerColorInteger) {
return -1;
return -1;
public boolean equals(Object o) {
if (this == o)
if (o == null
getClass() != o.getClass())
Poker poker = (Poker)
if (!this.content.equals(poker.content))
return this.color.equals(poker.color);
public int hashCode() {
int result = color.hashCode();
result = 31 * result + content.hashCode();
Player.java
public class Player implements Comparable {
private int ID;
private SortedSet&Poker& sortedP
public SortedSet&Poker& getSortedPoker() {
return sortedP
public void addSortedPoker(Poker sortedPoker) {
this.sortedPoker.add(sortedPoker);
public Player() {
sortedPoker = new TreeSet&&();
public int getID() {
return ID;
public void setID(int ID) {
this.ID = ID;
public String getName() {
public void setName(String name) {
this.name =
public String toString() {
return "玩家 {" +
"ID=" + ID +
", name='" + name + '\'' +
public boolean equals(Object o) {
if (this == o)
if (o == null
getClass() != o.getClass())
Player player = (Player)
return ID == player.ID;
public int hashCode() {
return ID;
public int compareTo(Object o) {
Player player = (Player)
if (this.getID() & player.getID()) {
} else if (this.getID() == player.getID()) {
} else if (this.getID() & player.getID()) {
return -1;
PlayGame.java
* 1.创建扑克牌,用到随机数,四个花色(黑桃,红桃,方片,梅花),数字(2-0),字母(J,Q,K,A),不含大小王,总计52张牌.
* 2.遍历生成的扑克牌,显示生成的扑克牌,显示花色和内容
* 3.洗牌,把生成的扑克牌顺序打乱
* 4.生成2个玩家,包括 ID,姓名
* 5.发牌,一人一张牌,从打乱顺序的牌的开头发牌,一人2张牌,就是前4张牌
* 6.开始游戏,显示2个玩家手中最大的手牌(先比较数字,数字相同时比较花色(黑桃&红桃&方片&梅花))
* 7.显示胜利者的名字
* 8.显示2个玩家名字及手牌
public class PokerGame {
List&Poker& pokerL
//洗牌以后的扑克牌
List&Poker& shufflerP
//创建2个玩家,ID不能相同
SortedSet&Player& playerS
//玩家手牌数量
private int playerHandPokerNumber = 2;
public void play() {
//创建52张扑克牌
createPoker();
pokerShuffle();
//遍历洗牌以后的扑克牌
iteratorAfterShuffle();
//创建2个玩家
createPlayer();
//遍历玩家
iteratorPlayer();
dealPoker();
//开始游戏
comparePlayerHandPoker();
//遍历玩家手牌
iteratorHandPoker();
* 创建扑克牌
private void createPoker() {
System.out.println("-----------创建扑克牌-----------");
List&String& pokerContent = new ArrayList&&(Const.pokerContent.keySet());
List&String& pokerColor=new ArrayList&&(Const.pokerColor.keySet());
pokerList = new ArrayList&&();
Poker temP
for (int i = 0; i & pokerColor.size(); i++) {
for (int j = 0; j & pokerContent.size(); j++) {
temPoker = new Poker();
temPoker.setColor(pokerColor.get(i));
temPoker.setContent(pokerContent.get(j));
pokerList.add(temPoker);
System.out.println("-----------扑克牌创建成功-----------");
iteratorPoker();
* 遍历扑克牌
public void iteratorPoker() {
for (int i = 0; i & pokerList.size(); i++) {
if (i == 0) {
System.out.print("为:[" + pokerList.get(i) + ", ");
if (i == pokerList.size() - 1) {
System.out.println(pokerList.get(i) + "]");
System.out.print(pokerList.get(i) + ", ");
public void pokerShuffle() {
System.out.println("-----------开始洗牌...-----------");
shufflerPoker = new ArrayList&&();
Random random = new Random();
while (shufflerPoker.size() & pokerList.size()) {
Poker poker = pokerList.get(random.nextInt(pokerList.size()));
if (shufflerPoker.contains(poker)) {
shufflerPoker.add(poker);
System.out.println("-----------洗牌结束!-----------");
* 洗牌结束后遍历牌
public void iteratorAfterShuffle() {
Iterator&Poker& iterator = shufflerPoker.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
* 创建 2 个玩家,要求 ID 必须为整数, ID不能重复。
private void createPlayer() {
playerSet = new TreeSet&&();
Scanner scanner = new Scanner(System.in);
System.out.println("-----------创建玩家...-----------");
Player temP
for (int i = 1; i &= 2; i++) {
//检查 ID 是否重复
boolean isDuplication =
while (!isDuplication) {
temPlayer = new Player();
System.out.println("请输入第 " + i + " 位玩家的ID和姓名:");
boolean isID =
while (!isID) {
System.out.println("输入ID:");
id = Integer.parseInt(scanner.next());
} catch (Exception e) {
System.out.println("请输入整数类型的ID!");
temPlayer.setID(id);
boolean isName =
while (!isName) {
System.out.println("输入姓名:");
name = scanner.next();
} catch (Exception e) {
System.out.println("请输入正确的玩家姓名!");
temPlayer.setName(name);
if (playerSet.contains(temPlayer)) {
isDuplication =
System.out.println("玩家ID已经被占用,请重新输入");
playerSet.add(temPlayer);
isDuplication =
* 遍历玩家,显示玩家姓名
private void iteratorPlayer() {
for (Player p : playerSet) {
System.out.println("---欢迎玩家:" + p.getName());
private void dealPoker() {
System.out.println("-----------开始发牌...-----------");
for (int i = 0; i & playerHandPokerNumber * playerSet.size(); i++) {
if (i % 2 == 0) {
player = playerSet.first();
player.addSortedPoker(shufflerPoker.get(i));
player = playerSet.last();
player.addSortedPoker(shufflerPoker.get(i));
System.out.println("----玩家:" + player.getName() + "-拿牌");
System.out.println("-----------发牌结束!-----------");
* 显示玩家的手牌
private void iteratorHandPoker() {
System.out.println("玩家各自的手牌为:");
for (Player p : playerSet) {
SortedSet&Poker& sortedPoker = p.getSortedPoker();
System.out.print(p.getName() + ":[");
for (Poker poker : sortedPoker) {
if (poker.equals(sortedPoker.last())) {
System.out.print(poker + "]" + "\n");
System.out.print(poker + ", ");
* 比较玩家的手牌
private void comparePlayerHandPoker() {
System.out.println("-----------开始游戏...-----------");
SortedMap&Poker, Player& playerHandMaxPoker = new TreeMap&&();
for (Player p : playerSet) {
Poker poker = p.getSortedPoker().last();
playerHandMaxPoker.put(poker, p);
System.out.println("玩家:" + p.getName() + "最大的手牌为:" + poker);
Poker maxPoker = playerHandMaxPoker.lastKey();
Player player = playerHandMaxPoker.get(maxPoker);
System.out.println("--------------玩家:" + player.getName() + "获胜!----------------");
public static void main(String[] args) {
PokerGame pokerGame = new PokerGame();
pokerGame.play();
-----------创建扑克牌-----------
-----------扑克牌创建成功-----------
为:[黑桃A, 黑桃J, 黑桃K, 黑桃Q, 黑桃2, 黑桃3, 黑桃4, 黑桃5, 黑桃6, 黑桃7, 黑桃8, 黑桃9, 黑桃10, 梅花A, 梅花J, 梅花K, 梅花Q, 梅花2, 梅花3, 梅花4, 梅花5, 梅花6, 梅花7, 梅花8, 梅花9, 梅花10, 方片A, 方片J, 方片K, 方片Q, 方片2, 方片3, 方片4, 方片5, 方片6, 方片7, 方片8, 方片9, 方片10, 红桃A, 红桃J, 红桃K, 红桃Q, 红桃2, 红桃3, 红桃4, 红桃5, 红桃6, 红桃7, 红桃8, 红桃9, 红桃10]
-----------开始洗牌...-----------
-----------洗牌结束!-----------
-----------创建玩家...-----------
请输入第 1 位玩家的ID和姓名:
请输入整数类型的ID!
输入姓名:
请输入第 2 位玩家的ID和姓名:
输入姓名:
---欢迎玩家:Tom
---欢迎玩家:Jack
-----------开始发牌...-----------
----玩家:Tom-拿牌
----玩家:Jack-拿牌
----玩家:Tom-拿牌
----玩家:Jack-拿牌
-----------发牌结束!-----------
-----------开始游戏...-----------
玩家:Tom最大的手牌为:梅花K
玩家:Jack最大的手牌为:梅花Q
--------------玩家:Tom获胜!----------------
玩家各自的手牌为:
Tom:[红桃6, 梅花K]
Jack:[黑桃6, 梅花Q]
若觉得本文不错,就分享一下吧!
作者的热门手记
请登录后,发表评论
评论加载中...
Copyright (C) 2018 imooc.com All Rights Reserved | 京ICP备 号-2中文(简体)
中文(繁體)
中文(台灣)
中文(新加坡)
中文(香港)
练习纸牌魔术基本技巧。所有的纸牌魔术家都知道怎样假装把一张牌塞进牌堆,然后从顶部翻出来来让观众吃惊。这个技巧用来练习手速、手指灵敏度、适时分散观众注意力和表演艺术绝对是不可或缺。用下面两种技巧来开始练习吧:
从最上面拿起两张牌,但要确保把两张牌放在一起(使它们看起来像一张牌)。
练习如何将整叠纸牌拿到身后或者拿进道具的短暂时间内,在不被人注意的情况下,偷偷把纸牌放进最上面那张牌的下方,放在正数第二张的位置。
邀请一位观众让他/她来抽出一张纸牌,“请您抽任意一张”。将选中的纸牌展示给所有人后,在不被人注意的时刻偷偷在身后将选中的纸牌放在正数第二张的位置。
如果有人怀疑你把牌藏在身后的举动,你可以辩解说这只是“悬念”的一部分,是“奇迹发生的一刻”——这个小技巧只是众多wikiHow纸牌魔术技巧中的其中之一。
重新展示纸牌给观众,同时拿着最上面的两张牌,使它们看起来像一张牌。仅把两张牌中的底下的牌展示给观众。
询问那位被邀请的观众:“这是不是你的牌?”请一定等到观众确认后再把两张牌放回整叠扑克牌的顶上。
把顶上第一张牌拿掉,插到扑克牌中任意处 (与此同时被选中的那张牌将被移到最顶上)。观众会以为被拿掉的那张才是他的牌。
解释说你会把被选中的纸牌变回到最顶上。你可以做出花哨的手势,来制造魔术的戏剧性效果。
翻开最顶上的牌,说“瞧!”,这就是观众选中的牌。这款魔术虽然不需要大量的练习,但是却会很让人费解。
从一副牌中挑出四个A,放在最上面。不要让观众看到这一步。
最好的做法就是变魔术之前就把四个A放在上面。把扑克从口袋里拿出,直接开始变魔术,不要让观众洗牌。
尽量做到不引人注意。可以随意地问,“嘿,你们想看个魔术吗?”然后直接开始变。这一步看起来越自然流畅,观众就越不会质疑这个魔术。
从底下往下分,将整副牌分为平均的四摞。四个A自然会出现在第四摞的顶上。
将四摞牌从左到右摆放,这样第四摞会出现在你的最右侧。
不要过于关注第四摞牌。魔术需要转移注意力。如果你的观众发现A在哪,这个魔术就没法进行了。
拿起第一摞牌,把顶上三张牌放到最下面。这能制造出你在洗牌的错觉。
将顶上的三张牌分到其他三摞牌上,每摞一张。从最左的一摞开始,带A的一摞最后。
每摞“只能”分1张牌。在分带A的一摞牌时,这一点至关重要,因为带A一摞上面必须有三张其他牌。
在其他三摞重复此步骤。最后做带A的一摞。
通过把顶上的三张牌放到最下,现在最后一摞最上又是四张A了。当你把最上的三张分到其他三摞后,每一摞上面就都是A了。
将每一摞最上面一张牌翻开,露出四张A。如果观众不相信,可以提出再变一次。
在你完全掌握了这个魔术之后,可以换观众来表演。向观众提供具体步骤指导,切牌(不要洗牌!),取牌(最上三张),分牌(每摞一张)。结果将是相同的。不同之处在于观众会更加相信你的魔术,因为你骗他们以为是他们在掌控结果。
拿出一副常规扑克牌,邀请一位观众洗牌。鼓励观众想洗多少次就洗多少次。这个魔术利用的是概率,不是障眼法。
让观众叫两张牌名。只要叫牌名(数字或字母),不要说具体花色。
例如,说“K”和“10”就可以了。说“黑桃K”和“红心10”会过于具体,碰在一起的几率就比较小。
当观众说“K”和“10”的时候,因为没有指出具体花色,所以实际上指的是每个数字对应的4张牌。两张牌指代的是总共8张可能的牌,即:方片K,梅花K,红心K,黑桃K,方片10,梅花10,红心10,和黑桃10。
这里的道理是:这8张牌中,至少有一个K会与10挨在一起。
把手放在牌上方,做出很用力的专注状。等待30秒至一分钟,再继续魔术。这样能让观众产生错觉,以为是你用魔法让这两张牌挨在一起的。
这一步是这个魔术中唯一需要做的肢体动作。尽可能少地与此魔术有肢体接触。这能加强观众认为是你真正制造出了这个假象的想法。
请观众翻开这副牌,并将其推成扇形寻找。这时观众会惊奇的发现那两张牌竟出现在一起了!
10%的情况下,K和10之间会隔着一张牌。若出现这种情况,只需告诉观众你注意力还不够集中。再表演一遍,很有可能这次这两张牌就会挨在一起了。
找到那两张牌,展示给观众。不要碰扑克牌,不然观众会以为是你悄悄插进了一张牌。
将一摞牌正面朝下放在一只手上。向观众展示,告诉观众你拿着的牌就是普普通通的牌。
向观众展示所有的牌,让他们觉得你没在牌上做手脚。开始魔术前你也可以自己洗,或邀请观众洗牌。
飞快地看一眼最底下的那张牌,然后正面朝下放下。你需要记住这张牌,因为之后向观众展示时你需要背出这张牌。
可以自己在心里默念“梅花6,梅花6,梅花6”。这能帮助你在接下来的表演中记住这张牌。
告诉观众,你要用食指一张张拨过这摞牌,观众可以随时叫停。这样能让观众产生错觉,让他们以为自己在魔术里是掌握主动权的。
假设你用左手托着这摞牌,牌面向下;然后用右手的大拇指贴在牌堆的下方,右手食指、中指把最上方的牌向自己拨动。
要是你右手拨过了超过四分之一的牌,观众还是没叫停,你可以稍微放慢点,调侃一下观众,看看他们会不会叫停你,这样你从底下抽牌可以容易一点。
当观众叫停时,将上面几张牌拿下。拿出大约整摞的四分之一。你拿的牌越多,那张你记下的牌就能伪装得越好。
用你的食指和中指拿下上面的那摞牌。将大拇指一直保持在整副牌最下面,不要让观众看见。这样你才能抽出之后要猜的那张秘密牌。
在拿走顶上的牌的同时,用连贯的动作,使用拇指托走底下的牌。注意只得拿走最底下的一张牌。
快速抽走那张牌。多数观众都不会注意到你的拇指是从最底下拿出来的。他们很可能以为最底下的牌来自这摞牌的中间。
向观众展示拿走这摞牌的最底下的一张,不要让自己看到。为了效果更好,可以闭上眼睛,或者向观众展示最后一张牌时看向别处。
然后询问观众,“最下面的牌是梅花6吗?”对于你成功猜对了牌,观众们一定会很好奇你怎么做到的。
将一摞纸牌面朝下摆成扇形。不需洗牌,虽然洗牌会让观众更放心。
邀请一位观众随意挑出一张牌。耐心等待,观众选牌选得越久,他就越会以为你不会猜出他选的牌。
为了使观众信服,观众选牌的时候将视线移开。很多观众以为猜牌靠的是花哨的数牌技巧。虽然有些魔术的确是这样的,但这个魔术其实更简单。
将牌分为两半,一手握一半。观众很可能从中间选牌,所以过后要用手将牌分为几摞。
告诉观众记住这张牌,然后放回牌堆中。讲话要缓慢,自信,清晰。
不要催促观众,不然他会以为你之前已经记住了牌。
飞快地看一眼右手摞最底下的牌。虽然你不用背出这张牌,但你将会用到这张牌帮你找到观众选的牌。
将观众选的牌放在两摞牌中间。确保要把右手那一摞放在上面,因为这样记住的那张牌就会紧挨着观众选的牌了。
将所有牌正面向上铺开。尽可能快地找到记住的那张牌。
将整摞牌连续铺开。最好的方法就是将整摞牌放在左侧,用右手轻轻地向右边铺开。最后牌面就像彩虹形状一样。
记住的牌应该在观众选牌的左手边。所以你记住的牌右边紧挨着的牌就是观众所选。
不要把牌过快或过于邋遢地铺开。你可能会不小心碰乱记住那张牌的位置,这样整个魔术就毁了。
你可以用手指,但不要停下盯着每张牌看。这样做可能会露出马脚,让观众发现你在干什么。
从牌堆中挑出那张牌,问观众“这是你的牌吗?”虽然这是个问题,但要自信得意地问。
要让观众以为你早在他选之前就知道他会选什么了。这会使你看起来有通灵预测能力,但其实你只是有个好记性罢了。这个魔术的诀窍就是记好参照牌的位置,让你可以推断出观众选的牌的位置。
记住牌堆上最上面的一张牌。比如说,“黑桃A”或“红桃7”。
这部分不要让观众看见。如果你从兜里直接掏出牌开始表演会更令人信服。
将这副牌面朝下放好,在上面蒙一条手帕。确保在盖上手帕前观众看到牌是面朝下的。
为了达到最好效果,确保手帕越不透光越好。
使用手帕是转移注意力的方式。人们会以为这款魔术使用的是视觉线索,这样就不会想到你已经在之前背下了牌。
在盖上手绢时,将整副牌翻成面朝上。确保在牌被盖住后再做。太早翻牌可能会破坏了整个魔术。
动作越稳越快越好。让盖手帕和翻牌的动作一气呵成,这样人们就只会看到表面上发生的动作了。
邀请一位观众,将这副盖着手帕的牌分成两半。让观众将上半部放在下半部的旁边。确保你知道哪个是哪个,而且牌需要一直盖着。
邀请观众将牌分为两半,不要洗牌。
将牌正面朝上,这样底下的一半就变成了上面的一半。这很重要,因为当你要观众将牌一分为二时,他们会错以为他在移动上半部,而实际上移动的是下半部分。
将真正的上半部分拿出手帕,同时要将其翻转为面朝下。真正的上半部应该包含你之前记忆过的最上面的那张牌。这有些难做,但如果你把注意力都吸引到手帕上也会很令人信服。
只能移动上半部分。手帕还要盖着下半部分,这部分还是面朝上的。
挥舞将要拿掉手帕的手。试着做一些花哨的动作,将观众的注意力从另一只手上移开。另一只手将会把牌翻转过来。
邀请观众把你拿出那摞牌的第一张抽出。指导他/她向其他观众展示,并不要让你自己看到。
实际上,这还是牌堆最上面的一张牌。但观众会以为这是牌堆中间的一张。
在所有人都看过后,说出牌的名字。等着听人们难以置信的惊叹声吧。
将剩下的一半从手帕里拿出,同时要将它翻转为面朝上。在观众还在思考你怎么做到的时候做这一步。
结束后,观众很可能想要检查另一半牌。不要给他们怀疑是不是牌在手帕下已经被翻过来了的机会。
在牌堆里先准备好“8”:在牌堆里把四张8找出来。牌堆面朝下,把一个8放在牌堆最上方,将第二个8放在从上往下数的第十张。
将牌翻过来,从上往下数7张牌。下两张8将放在第8第9的位置。再把牌翻过来,就可以开始了。
做出一个大动作,从头到尾过一遍牌。做的时候,说你会做出一个很奇妙的预测。
和观众说话的时候把牌堆展成扇形耍一耍酷,然后再把牌堆整齐。
开始把牌从一只手展开到另一只手呈扇形,悄悄数到第十张,不要直视纸牌,而是要看着观众,谈话不停。到第十张之后,食指贴在牌下,并继续把牌展开。
把第十张牌抽出来(其中一张8),然后背面朝上放在桌子上,和观众说这是你猜的那张牌。
将整副牌翻过来。告诉大家你要数牌。快速先数着,等你数过了第7和第8张(两个8)之后,再告诉观众,“现在你们可以随意喊停”。
当观众喊停后,把牌分为两摞,两摞牌都翻过来背面朝上放在桌上。原先更底下的那叠放在右边(就是第八第九张牌都是8的那部分),更上面的那叠(8放在顶部的)放在左边。
将左侧牌堆顶部的牌翻过来,这就是之前布置在顶部的8,告诉观众这是某某花色的8。接下来说,牌堆上的8,意味着你应该从右侧的牌堆里拿出8张牌。
数出8张牌,把这叠牌放在桌上原先放在左边的牌堆旁边。右边那堆牌还是抓在手里,牌面向下。
引领观众,把新的一叠放在桌上的时候,大声数“一,二,三”。告诉观众现在有三堆牌,他们已经见到一张8 了。
展示其他的8:把你刚放在桌上的牌整个翻过来,是个8,放在第一个揭示的8旁边。
然后,把另一叠牌面朝下的第一张反转过来,也是一个8。和前两个8放一块。
最后,你已经吊起观众十足的胃口,可以突然翻起之前的“预测牌”(一直背面朝下)。或者让观众来翻!
观众的反应是让你忘不了的。这个把戏骗过了很多人的眼睛!
本页面已经被访问过2,286,154次。10000元/套
10000元/套
10000元/套
10000元/套
10000元/套
10000元/套
产品供应分类
求购信息分类
企业商铺分类
“十一五”至“十二五”期间,国务院出台十项措施扩大..
18-0117-1217-0617-0617-0517-0517-0517-04
鲁蒙VRA防腐涂料是无毒环保、防水、防潮、..
09-2709-0106-1405-2804-29
从2015年7月开始,深圳市开始实行全面禁用油漆的政策,..
姓名: 专业: 姓名: 专业: 姓名: 专业: 姓名: 专业: 姓名: 专业: 姓名: 专业:常用排序算法总结(一) - SteveWang - 博客园
尘世中一个迷途小书童,读书太少,想得太多
posts - 72, comments - 34, trackbacks - 0, articles - 0
  我们通常所说的排序算法往往指的是内部排序算法,即数据记录在内存中进行排序。
  排序算法大体可分为两种:
    一种是比较排序,时间复杂度O(nlogn) ~ O(n^2),主要有:冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序等。
    另一种是非比较排序,时间复杂度可以达到O(n),主要有:计数排序,基数排序,桶排序等。
  这里我们来探讨一下常用的比较排序算法,非比较排序算法将在中介绍。下表给出了常见比较排序算法的性能:
  有一点我们很容易忽略的是排序算法的稳定性(腾讯校招2016笔试题曾考过)。
  排序算法稳定性的简单形式化定义为:如果Ai = Aj,排序前Ai在Aj之前,排序后Ai还在Aj之前,则称这种排序算法是稳定的。通俗地讲就是保证排序前后两个相等的数的相对顺序不变。
  对于不稳定的排序算法,只要举出一个实例,即可说明它的不稳定性;而对于稳定的排序算法,必须对算法进行分析从而得到稳定的特性。需要注意的是,排序算法是否为稳定的是由具体算法决定的,不稳定的算法在某种条件下可以变为稳定的算法,而稳定的算法在某种条件下也可以变为不稳定的算法。
  例如,对于冒泡排序,原本是稳定的排序算法,如果将记录交换的条件改成A[i] &= A[i + 1],则两个相等的记录就会交换位置,从而变成不稳定的排序算法。
  其次,说一下排序算法稳定性的好处。排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,前一个键排序的结果可以为后一个键排序所用。基数排序就是这样,先按低位排序,逐次按高位排序,低位排序后元素的顺序在高位也相同时是不会改变的。
  冒泡排序(Bubble Sort)
  冒泡排序是一种极其简单的排序算法,也是我所学的第一个排序算法。它重复地走访过要排序的元素,依次比较相邻两个元素,如果他们的顺序错误就把他们调换过来,直到没有元素再需要交换,排序完成。这个算法的名字由来是因为越小(或越大)的元素会经由交换慢慢&浮&到数列的顶端。
  冒泡排序算法的运作如下:
比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
  由于它的简洁,冒泡排序通常被用来对于程序设计入门的学生介绍算法的概念。冒泡排序的代码如下:
#include &stdio.h&
// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(n^2)
// 最优时间复杂度 ---- 如果能在内部循环第一次运行时,使用一个旗标来表示有无需要交换的可能,可以把最优时间复杂度降低到O(n)
// 平均时间复杂度 ---- O(n^2)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 稳定
void Swap(int A[], int i, int j)
int temp = A[i];
A[i] = A[j];
void BubbleSort(int A[], int n)
for (int j = 0; j & n - 1; j++)
// 每次最大元素就像气泡一样"浮"到数组的最后
for (int i = 0; i & n - 1 - i++) // 依次比较相邻的两个元素,使较大的那个向后移
if (A[i] & A[i + 1])
// 如果条件改成A[i] &= A[i + 1],则变为不稳定的排序算法
Swap(A, i, i + 1);
int main()
int A[] = { 6, 5, 3, 1, 8, 7, 2, 4 };
// 从小到大冒泡排序
int n = sizeof(A) / sizeof(int);
BubbleSort(A, n);
printf("冒泡排序结果:");
for (int i = 0; i & i++)
printf("%d ", A[i]);
printf("\n");
  上述代码对序列{ 6, 5, 3, 1, 8, 7, 2, 4 }进行冒泡排序的实现过程如下
  使用冒泡排序为一列数字进行排序的过程如右图所示:  
  尽管冒泡排序是最容易了解和实现的排序算法之一,但它对于少数元素之外的数列排序是很没有效率的。
  冒泡排序的改进:鸡尾酒排序
  鸡尾酒排序,也叫定向冒泡排序,是冒泡排序的一种改进。此算法与冒泡排序的不同处在于从低到高然后从高到低,而冒泡排序则仅从低到高去比较序列里的每个元素。他可以得到比冒泡排序稍微好一点的效能。
  鸡尾酒排序的代码如下:
#include &stdio.h&
// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(n^2)
// 最优时间复杂度 ---- 如果序列在一开始已经大部分排序过的话,会接近O(n)
// 平均时间复杂度 ---- O(n^2)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 稳定
void Swap(int A[], int i, int j)
int temp = A[i];
A[i] = A[j];
void CocktailSort(int A[], int n)
int left = 0;
// 初始化边界
int right = n - 1;
while (left & right)
for (int i = i & i++)
// 前半轮,将最大元素放到后面
if (A[i] & A[i + 1])
Swap(A, i, i + 1);
for (int i = i & i--)
// 后半轮,将最小元素放到前面
if (A[i - 1] & A[i])
Swap(A, i - 1, i);
int main()
int A[] = { 6, 5, 3, 1, 8, 7, 2, 4 };
// 从小到大定向冒泡排序
int n = sizeof(A) / sizeof(int);
CocktailSort(A, n);
printf("鸡尾酒排序结果:");
for (int i = 0; i & i++)
printf("%d ", A[i]);
printf("\n");
  使用鸡尾酒排序为一列数字进行排序的过程如右图所示:  
  以序列(2,3,4,5,1)为例,鸡尾酒排序只需要访问一次序列就可以完成排序,但如果使用冒泡排序则需要四次。但是在乱数序列的状态下,鸡尾酒排序与冒泡排序的效率都很差劲。
  选择排序(Selection Sort)
  选择排序也是一种简单直观的排序算法。它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
  注意选择排序与冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置;而选择排序每遍历一次都记住了当前最小(大)元素的位置,最后仅需一次交换操作即可将其放到合适的位置。
  选择排序的代码如下:
#include &stdio.h&
// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(n^2)
// 最优时间复杂度 ---- O(n^2)
// 平均时间复杂度 ---- O(n^2)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 不稳定
void Swap(int A[], int i, int j)
int temp = A[i];
A[i] = A[j];
void SelectionSort(int A[], int n)
for (int i = 0; i & n - 1; i++)
// i为已排序序列的末尾
for (int j = i + 1; j & j++)
// 未排序序列
if (A[j] & A[min])
// 找出未排序序列中的最小值
if (min != i)
Swap(A, min, i);
// 放到已排序序列的末尾,该操作很有可能把稳定性打乱,所以选择排序是不稳定的排序算法
int main()
int A[] = { 8, 5, 2, 6, 9, 3, 1, 4, 0, 7 }; // 从小到大选择排序
int n = sizeof(A) / sizeof(int);
SelectionSort(A, n);
printf("选择排序结果:");
for (int i = 0; i & i++)
printf("%d ", A[i]);
printf("\n");
&  上述代码对序列{ 8, 5, 2, 6, 9, 3, 1, 4, 0, 7 }进行选择排序的实现过程如右图  
  使用选择排序为一列数字进行排序的宏观过程:  
  选择排序是不稳定的排序算法,不稳定发生在最小元素与A[i]交换的时刻。
  比如序列:{ 5, 8, 5, 2, 9 },一次选择的最小元素是2,然后把2和第一个5进行交换,从而改变了两个元素5的相对次序。
  插入排序(Insertion Sort)
  插入排序是一种简单直观的排序算法。它的工作原理非常类似于我们抓扑克牌
      
  对于未排序数据(右手抓到的牌),在已排序序列(左手已经排好序的手牌)中从后向前扫描,找到相应位置并插入。
  插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
  具体算法描述如下:
从第一个元素开始,该元素可以认为已经被排序
取出下一个元素,在已经排序的元素序列中从后向前扫描
如果该元素(已排序)大于新元素,将该元素移到下一位置
重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
将新元素插入到该位置后
重复步骤2~5
  插入排序的代码如下:
#include &stdio.h&
// 分类 ------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- 最坏情况为输入序列是降序排列的,此时时间复杂度O(n^2)
// 最优时间复杂度 ---- 最好情况为输入序列是升序排列的,此时时间复杂度O(n)
// 平均时间复杂度 ---- O(n^2)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 稳定
void InsertionSort(int A[], int n)
for (int i = 1; i & i++)
// 类似抓扑克牌排序
int get = A[i];
// 右手抓到一张扑克牌
int j = i - 1;
// 拿在左手上的牌总是排序好的
while (j &= 0 && A[j] & get)
// 将抓到的牌与手牌从右向左进行比较
A[j + 1] = A[j];
// 如果该手牌比抓到的牌大,就将其右移
A[j + 1] = get; // 直到该手牌比抓到的牌小(或二者相等),将抓到的牌插入到该手牌右边(相等元素的相对次序未变,所以插入排序是稳定的)
int main()
int A[] = { 6, 5, 3, 1, 8, 7, 2, 4 };// 从小到大插入排序
int n = sizeof(A) / sizeof(int);
InsertionSort(A, n);
printf("插入排序结果:");
for (int i = 0; i & i++)
printf("%d ", A[i]);
printf("\n");
&  上述代码对序列{ 6, 5, 3, 1, 8, 7, 2, 4 }进行插入排序的实现过程如下
      
      
  使用插入排序为一列数字进行排序的宏观过程:  
  插入排序不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,比如量级小于千,那么插入排序还是一个不错的选择。 插入排序在工业级库中也有着广泛的应用,在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充,用于少量元素的排序(通常为8个或以下)。
  插入排序的改进:二分插入排序
  对于插入排序,如果比较操作的代价比交换操作大的话,可以采用二分查找法来减少比较操作的次数,我们称为二分插入排序,代码如下:
#include &stdio.h&
// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(n^2)
// 最优时间复杂度 ---- O(nlogn)
// 平均时间复杂度 ---- O(n^2)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 稳定
void InsertionSortDichotomy(int A[], int n)
for (int i = 1; i & i++)
int get = A[i];
// 右手抓到一张扑克牌
int left = 0;
// 拿在左手上的牌总是排序好的,所以可以用二分法
int right = i - 1;
// 手牌左右边界进行初始化
while (left &= right)
// 采用二分法定位新牌的位置
int mid = (left + right) / 2;
if (A[mid] & get)
right = mid - 1;
left = mid + 1;
for (int j = i - 1; j &= j--)
// 将欲插入新牌位置右边的牌整体向右移动一个单位
A[j + 1] = A[j];
A[left] = get;
// 将抓到的牌插入手牌
int main()
int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };// 从小到大二分插入排序
int n = sizeof(A) / sizeof(int);
InsertionSortDichotomy(A, n);
printf("二分插入排序结果:");
for (int i = 0; i & i++)
printf("%d ", A[i]);
printf("\n");
  当n较大时,二分插入排序的比较次数比直接插入排序的最差情况好得多,但比直接插入排序的最好情况要差,所当以元素初始序列已经接近升序时,直接插入排序比二分插入排序比较次数少。二分插入排序元素移动次数与直接插入排序相同,依赖于元素初始序列。
  插入排序的更高效改进:希尔排序(Shell Sort)
  希尔排序,也叫递减增量排序,是插入排序的一种更高效的改进版本。希尔排序是不稳定的排序算法。
  希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
  希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。  假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n^2)的排序(冒泡排序或直接插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。
  希尔排序的代码如下:
#include &stdio.h&
// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- 根据步长序列的不同而不同。已知最好的为O(n(logn)^2)
// 最优时间复杂度 ---- O(n)
// 平均时间复杂度 ---- 根据步长序列的不同而不同。
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 不稳定
void ShellSort(int A[], int n)
int h = 0;
while (h &= n)
// 生成初始增量
h = 3 * h + 1;
while (h &= 1)
for (int i = i & i++)
int j = i -
int get = A[i];
while (j &= 0 && A[j] & get)
A[j + h] = A[j];
A[j + h] = get;
h = (h - 1) / 3;
// 递减增量
int main()
int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };// 从小到大希尔排序
int n = sizeof(A) / sizeof(int);
ShellSort(A, n);
printf("希尔排序结果:");
for (int i = 0; i & i++)
printf("%d ", A[i]);
printf("\n");
  以23, 10, 4, 1的步长序列进行希尔排序:  
  希尔排序是不稳定的排序算法,虽然一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱。
  比如序列:{&3, 5,&10,&8, 7, 2, 8, 1, 20, 6 },h=2时分成两个子序列&{&3, 10, 7, 8, 20 } 和&&{&5, 8, 2, 1, 6 } ,未排序之前第二个子序列中的8在前面,现在对两个子序列进行插入排序,得到&{&3,&7,&8,&10, 20 } 和 {&1,&2,&5,&6,&8&} ,即&{&3, 1,&7,&2, 8, 5,&10, 6, 20, 8 } ,两个8的相对次序发生了改变。
  归并排序(Merge Sort)
  归并排序是创建在归并操作上的一种有效的排序算法,效率为O(nlogn),1945年由冯&诺伊曼首次提出。
  归并排序的实现分为递归实现与非递归(迭代)实现。递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。
  归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个序列的操作,归并操作步骤如下:
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
设定两个指针,最初位置分别为两个已经排序序列的起始位置
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针到达序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
  归并排序的代码如下:
#include &stdio.h&
#include &limits.h&
// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(nlogn)
// 最优时间复杂度 ---- O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ O(n)
// 稳定性 ------------ 稳定
void Merge(int A[], int left, int mid, int right)// 合并两个已排好序的数组A[left...mid]和A[mid+1...right]
int len = right - left + 1;
int *temp = new int[len];
// 辅助空间O(n)
int index = 0;
// 前一数组的起始元素
int j = mid + 1;
// 后一数组的起始元素
while (i &= mid && j &= right)
temp[index++] = A[i] &= A[j] ? A[i++] : A[j++];
// 带等号保证归并排序的稳定性
while (i &= mid)
temp[index++] = A[i++];
while (j &= right)
temp[index++] = A[j++];
for (int k = 0; k & k++)
A[left++] = temp[k];
void MergeSortRecursion(int A[], int left, int right)
// 递归实现的归并排序(自顶向下)
if (left == right)
// 当待排序的序列长度为1时,递归开始回溯,进行merge操作
int mid = (left + right) / 2;
MergeSortRecursion(A, left, mid);
MergeSortRecursion(A, mid + 1, right);
Merge(A, left, mid, right);
void MergeSortIteration(int A[], int len)
// 非递归(迭代)实现的归并排序(自底向上)
int left, mid,// 子数组索引,前一个为A[left...mid],后一个子数组为A[mid+1...right]
for (int i = 1; i & i *= 2)
// 子数组的大小i初始为1,每轮翻倍
while (left + i & len)
// 后一个子数组存在(需要归并)
mid = left + i - 1;
right = mid + i & len ? mid + i : len - 1;// 后一个子数组大小可能不够
Merge(A, left, mid, right);
left = right + 1;
// 前一个子数组索引向后移动
int main()
int A1[] = { 6, 5, 3, 1, 8, 7, 2, 4 };
// 从小到大归并排序
int A2[] = { 6, 5, 3, 1, 8, 7, 2, 4 };
int n1 = sizeof(A1) / sizeof(int);
int n2 = sizeof(A2) / sizeof(int);
MergeSortRecursion(A1, 0, n1 - 1);
// 递归实现
MergeSortIteration(A2, n2);
// 非递归实现
printf("递归实现的归并排序结果:");
for (int i = 0; i & n1; i++)
printf("%d ", A1[i]);
printf("\n");
printf("非递归实现的归并排序结果:");
for (int i = 0; i & n2; i++)
printf("%d ", A2[i]);
printf("\n");
  上述代码对序列{ 6, 5, 3, 1, 8, 7, 2, 4 }进行归并排序的实例如下 
     
  使用归并排序为一列数字进行排序的宏观过程:    
  归并排序除了可以对数组进行排序,还可以高效的求出数组小和(即单调和)以及数组中的逆序对,详见这篇。
  堆排序(Heap Sort)
  堆排序是指利用堆这种数据结构所设计的一种选择排序算法。堆是一种近似完全二叉树的结构(通常堆是通过一维数组来实现的),并满足性质:以最大堆(也叫大根堆、大顶堆)为例,其中父结点的值总是大于它的孩子节点。
  我们可以很容易的定义堆排序的过程:
由输入的无序数组构造一个最大堆,作为初始的无序区
把堆顶元素(最大值)和堆尾元素互换
把堆(无序区)的尺寸缩小1,并调用heapify(A, 0)从新的堆顶元素开始进行堆调整
重复步骤2,直到堆的尺寸为1
  堆排序的代码如下:
#include &stdio.h&
// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(nlogn)
// 最优时间复杂度 ---- O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 不稳定
void Swap(int A[], int i, int j)
int temp = A[i];
A[i] = A[j];
void Heapify(int A[], int i, int size)
// 从A[i]向下进行堆调整
int left_child = 2 * i + 1;
// 左孩子索引
int right_child = 2 * i + 2;
// 右孩子索引
// 选出当前结点与其左右孩子三者之中的最大值
if (left_child & size && A[left_child] & A[max])
max = left_
if (right_child & size && A[right_child] & A[max])
max = right_
if (max != i)
Swap(A, i, max);
// 把当前结点和它的最大(直接)子节点进行交换
Heapify(A, max, size);
// 递归调用,继续从当前结点向下进行堆调整
int BuildHeap(int A[], int n)
// 建堆,时间复杂度O(n)
int heap_size =
for (int i = heap_size / 2 - 1; i &= 0; i--) // 从每一个非叶结点开始向下进行堆调整
Heapify(A, i, heap_size);
return heap_
void HeapSort(int A[], int n)
int heap_size = BuildHeap(A, n);
// 建立一个最大堆
while (heap_size & 1)
       // 堆(无序区)元素个数大于1,未完成排序
// 将堆顶元素与堆的最后一个元素互换,并从堆中去掉最后一个元素
// 此处交换操作很有可能把后面元素的稳定性打乱,所以堆排序是不稳定的排序算法
Swap(A, 0, --heap_size);
Heapify(A, 0, heap_size);
// 从新的堆顶元素开始向下进行堆调整,时间复杂度O(logn)
int main()
int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };// 从小到大堆排序
int n = sizeof(A) / sizeof(int);
HeapSort(A, n);
printf("堆排序结果:");
for (int i = 0; i & i++)
printf("%d ", A[i]);
printf("\n");
  堆排序算法的演示:  
  动画中在排序过程之前简单的表现了创建堆的过程以及堆的逻辑结构。
  堆排序是不稳定的排序算法,不稳定发生在堆顶元素与A[i]交换的时刻。
  比如序列:{ 9, 5, 7,&5 },堆顶元素是9,堆排序下一步将9和第二个5进行交换,得到序列&{&5, 5, 7,&9&},再进行堆调整得到{&7, 5,&5,&9&},重复之前的操作最后得到{ 5,&5, 7,&9&}从而改变了两个5的相对次序。
  快速排序(Quick Sort)
  快速排序是由东尼&霍尔所发展的一种排序算法。在平均状况下,排序n个元素要O(nlogn)次比较。在最坏状况下则需要O(n^2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他O(nlogn)算法更快,因为它的内部循环可以在大部分的架构上很有效率地被实现出来。
  快速排序使用分治策略(Divide and Conquer)来把一个序列分为两个子序列。步骤为:
从序列中挑出一个元素,作为"基准"(pivot).
把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个称为分区(partition)操作。
对每个分区递归地进行步骤1~2,递归的结束条件是序列的大小是0或1,这时整体已经被排好序了。
  快速排序的代码如下:
#include &stdio.h&
// 分类 ------------ 内部比较排序
// 数据结构 --------- 数组
// 最差时间复杂度 ---- 每次选取的基准都是最大(或最小)的元素,导致每次只划分出了一个分区,需要进行n-1次划分才能结束递归,时间复杂度为O(n^2)
// 最优时间复杂度 ---- 每次选取的基准都是中位数,这样每次都均匀的划分出两个分区,只需要logn次划分就能结束递归,时间复杂度为O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ 主要是递归造成的栈空间的使用(用来保存left和right等局部变量),取决于递归树的深度,一般为O(logn),最差为O(n)
// 稳定性 ---------- 不稳定
void Swap(int A[], int i, int j)
int temp = A[i];
A[i] = A[j];
int Partition(int A[], int left, int right)
// 划分函数
int pivot = A[right];
// 这里每次都选择最后一个元素作为基准
int tail = left - 1;
// tail为小于基准的子数组最后一个元素的索引
for (int i = i & i++)
// 遍历基准以外的其他元素
if (A[i] &= pivot)
// 把小于等于基准的元素放到前一个子数组末尾
Swap(A, ++tail, i);
Swap(A, tail + 1, right);
// 最后把基准放到前一个子数组的后边,剩下的子数组既是大于基准的子数组
// 该操作很有可能把后面元素的稳定性打乱,所以快速排序是不稳定的排序算法
return tail + 1;
// 返回基准的索引
void QuickSort(int A[], int left, int right)
if (left &= right)
int pivot_index = Partition(A, left, right); // 基准的索引
QuickSort(A, left, pivot_index - 1);
QuickSort(A, pivot_index + 1, right);
int main()
int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 }; // 从小到大快速排序
int n = sizeof(A) / sizeof(int);
QuickSort(A, 0, n - 1);
printf("快速排序结果:");
for (int i = 0; i & i++)
printf("%d ", A[i]);
printf("\n");
  使用快速排序法对一列数字进行排序的过程:  
  快速排序是不稳定的排序算法,不稳定发生在基准元素与A[tail+1]交换的时刻。
  比如序列:{ 1, 3, 4, 2, 8, 9, 8, 7, 5 },基准元素是5,一次划分操作后5要和第一个8进行交换,从而改变了两个元素8的相对次序。
  Java系统提供的Arrays.sort函数。对于基础类型,底层使用快速排序。对于非基础类型,底层使用归并排序。请问是为什么?
  答:这是考虑到排序算法的稳定性。对于基础类型,相同值是无差别的,排序前后相同值的相对位置并不重要,所以选择更为高效的快速排序,尽管它是不稳定的排序算法;而对于非基础类型,排序前后相等实例的相对位置不宜改变,所以选择稳定的归并排序。&

我要回帖

更多关于 扑克牌中的k代表什么 的文章

 

随机推荐