算法初探 之 排序算法
摘《李开复:算法的力量》 : 算法是计算机科学领域最重要的基石之一,但却受到了国内一些程序员的冷落。许多学生看到一些公司在招聘时要求的编程语言五花八门就产生了一种误解,认为学计算机就是学各种编程语言,或者认为,学习最新的语言、技术、标准就是最好的铺路方法。其实大家都被这些公司误导了。编程语言虽然该学,但是学习计算机算 法和理论更重要,因为计算机算法和理论更重要,因为计算机语言和开发平台日新月异,但万变不离其宗的是那些算法和理论,例如数据结构、算法、编译原理、计算机体系结构、关系型数据库原理等等。
许多计算机科学家认为,排序算法是算法学习中最基本的问题。那么我们就从排序这类算法开始,去看看算法究竟是什么。
排序算法解决的是一类什么具体问题呢?
输入:n个数的序列<a1,a2,a3,...,an>
输出:输入序列的一个重排<a'1,a'2,a'3,...,a'n>,使得a'1<=a'2<=a'3<=...<=a'n
输入序列通常是一个n元数组,但也可能由其他形式来表示,如链表。
对于这个问题,我们可以很容易联想到日常生活中的许多例子。那么我们解决这个问题的第一个思路便可以从日常生活中获得。
插入排序,这是一个对少量元素进行排序的有效算法。其工作机理和很多人打牌时,整理手中牌时的做法差不多。在开始摸牌时,我们的左手是空的,牌面朝下的放在桌上。接着,一次从桌上摸起一张牌,并将它插入到左手一把牌中的正确位置上。为了找到这张牌合适的位置,要将它与手中已有的每一张牌从右向左地进行比较。无论在什么时候,左手中的牌都是排好序的,而这些牌原先都是桌上那副牌里最顶上的一些牌。
//主方法
INSERTION-SORT(A)
//从桌面上一次次取牌
for j ← 2 to length[A]
do
//取一张牌到右手key
key ← A[j]
//眼睛注意的位置(将要比较的位置)
i ← j – 1
//开始寻找合适的位置
while i > 0 and A[i] > key
do
//微调左手中的已经排好顺序的牌
A[i + 1] ← A[i]
//移动眼睛,转换比较的对象
i ← i – 1
//将右手中的牌放在左手牌中合适的位置上
A[i + 1] ← key
大概的思路已经在脑子中形成了,剩下的是简单的工作:将思路转化为实实在在的代码。在这里我用java编写了一个静态方法。关于这个算法的具体证明过程详见《Introduction to Algorithms》.
*InsertionSort
*The time limit of this sort is O(n^2)
*<strong>O(n^2)</strong>
*@param element will be sorted
*在这段代码中使用了java的范型以及一个对象接口,详细情况可以参见java相关教材
*/
public static < Type extends Comparable > void insertionSort(Type element[]) ... {
for(int j=1;j<element.length;j++)...{
Type key = element[j];
int i = j-1;
while( (i>=0)&&(element[i].compareTo(key)>0))...{
element[i+1] = element[i];
i--;
}
element[i+1] = key;
}
}
如同大家打牌一样,很多人熟能生巧整理手中的牌时又准又快,而有些人却费时费力。那么一个算法也是友好有坏,在这里我只给出相关的评价指数,至于具体规定可参见相关书籍。
这个算法的时间复杂度为O(n^2),空间复杂度为O(n).关于“O”符号可以在微积分类书籍上找到更为详尽的解释。
科学技术不断进步,很多新的算法应运而生。在这里再介绍一种排序算法。它在时间复杂度上有了具大提高,空间上却付出了两倍的代价。
合并排序,一种利用了“分治策略”的排序方法。分治策略:将原问题划分成n个规模较小而结构简单与原问题相似的子问题;递归地解决这些子问题,然后再合并其结果,就得到原问题的解。排序算法就是借助了这种思想,本质是:从中间把数组分成元素个数尽量相等的两半,分别对他们进行排序,然后再合并两个有序表。整个问题最困难的地方便是合并两个有序表,在这里我们着重看看这个过程。
再举打牌这个例子,假设有两堆牌正面朝上地放在桌上,每一堆都是已经排好顺序的,最小的牌在最上面。我们希望把这两堆牌合并成一个排好序的输出堆,面朝下地放在桌上。基本步骤包括在面朝上的两堆牌中,选取顶上两张牌中较小的一张,将其取出后面朝下地放入输出堆中。重复这个步骤,直到某一堆为空为止。
*Merge used in the mergeSort
*<strong>O(n)</strong>
*@param element Type[] the elements to be merged
*@param p int the begin of the first elements
*@param q int the end of the first elements
*@param r int the end of the second elements
*/
private static <Type extends Comparable> void merge(Type element[],int p,int q,int r)...{
//确定两堆牌的起始位置
int nl = q-p+1;
int nr = r-q;
//新建两个输入堆用于存放待组合牌
Type[] lElement,rElement;
lElement = (Type[])java.lang.reflect.Array.newInstance(element.getClass().getComponentType(),nl);
rElement = (Type[])java.lang.reflect.Array.newInstance(element.getClass().getComponentType(),nr);
//做记录处理
for(int i=0;i<nl;i++)...{
lElement[i] = element[p+i];
}
for(int i=0;i<nr;i++)...{
rElement[i] = element[q+i+1];
}
//开始取牌
int i=0,j=0;
int k = p;
//逐个比较,一直到一个堆为空
while((i<nl)&&(j<nr))...{
if(lElement[i].compareTo(rElement[j])<=0)...{
element[k] = lElement[i];
i++;
}else...{
element[k] = rElement[j];
j++;
}
k++;
}
//处理某一堆中 剩下的牌
while(i<nl)...{
element[k] = lElement[i];
i++;
k++;
}
while(j<nr)...{
element[k] = rElement[j];
j++;
k++;
}
}
这个算法的时间复杂度为O(n log2 n),空间复杂度为O(2n)=O(n).
至此两个算法的介绍结束,我们将这类算法扩大化,从中取出根本的东西。
逆序对
逆序对是指在一个元素序列中,按照一定的大小比较方法,序列中任两个元素大小顺序颠倒的组合。
设A[1..n]是一个包含n个不同数的数组.如果在i<j的情况下,有A[i]>A[j],则(i,j)就称为A中的一个逆序对.
对于一个给定待排序序列,其中的逆序对的发现与还原正是排序所要解决的事情.排序过程中一般是先发现逆序对再将其还原,由于这些让我们联想到了排序性能提高的一些方法.
1.一次还原操作,使得多组逆序对还原.
eg: 共有21对 共有10对
7,6,5,4,3,2,1--(1与7互换)-->1,6,5,4,3,2,7
一次还原处理了11对
2.一次对换操作后,尽量不要或者少产生额外的逆序对
eg: 共有16对 共有11对
7,6,5,1,3,2,4--(4与7互换)-->4,6,5,1,3,2,7
一次还原处理了5对,另外又多产生了(4,1)一对
在我观察的比较类排序中,快速排序体现了1却缺失了2;合并排序恰恰相反体现了2却缺失了1.
关于逆序对的相关统计算法代码参见
* Copyright (C) 2000-2007 Wang Pengcheng <wpc0000@163.com>
* Licensed to the Wang Pengcheng under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The LGPL licenses this file to You under the GNU Lesser General Public
* Licence, Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package mypackage.algorithm.unit02;
public class Inversion ...{
private static int tot;
/** *//**
*InsertionSort
*The time limit of this sort is O(n^2)
*<strong>O(n^2)</strong>
*@param element will be sorted
*/
private static <Type extends Comparable> void insertionSort(Type element[])...{
for(int j=1;j<element.length;j++)...{
Type key = element[j];
int i = j-1;
while( (i>=0)&&(element[i].compareTo(key)>0))...{
element[i+1] = element[i];
tot++;
i--;
}
element[i+1] = key;
}
}
/** *//**
*Merge used in the mergeSort
*<strong>O(n)</strong>
*@param element Type[] the elements to be merged
*@param p int the begin of the first elements
*@param q int the end of the first elements
*@param r int the end of the second elements
*/
private static <Type extends Comparable> void merge(Type element[],int p,int q,int r)...{
int nl = q-p+1;
int nr = r-q;
Type[] lElement,rElement;
lElement = (Type[])java.lang.reflect.Array.newInstance(element.getClass().getComponentType(),nl);
rElement = (Type[])java.lang.reflect.Array.newInstance(element.getClass().getComponentType(),nr);
for(int i=0;i<nl;i++)...{
lElement[i] = element[p+i];
}
for(int i=0;i<nr;i++)...{
rElement[i] = element[q+i+1];
}
int i=0,j=0;
int k = p;
while((i<nl)&&(j<nr))...{
if(lElement[i].compareTo(rElement[j])<=0)...{
element[k] = lElement[i];
i++;
}else...{
element[k] = rElement[j];
j++;
tot+=nl-i;
}
k++;
}
while(i<nl)...{
element[k] = lElement[i];
i++;
k++;
}
while(j<nr)...{
element[k] = rElement[j];
j++;
k++;
}
}
/** *//**
*mergeSort
*Sort the elements by the compareTo method
*<strong>O(nlgn)</strong>
*@param element Type[] that will be sorted
*@param p the begin of the list will be sorted
*@param r the end of the list will be sorted
*/
private static <Type extends Comparable> void mergeSort(Type element[],int p,int r)...{
if(p<r)...{
int q = (p+r)/2;
mergeSort(element,p,q);
mergeSort(element,q+1,r);
merge(element,p,q,r);
}
}
private static <Type extends Comparable> void mergeSort(Type element[])...{
mergeSort(element,0,element.length-1);
}
/** *//**
* Count the inversion number by O(nlgn)
* @param <Type> inversion number type
* @param element inversion number list
* @return count number of inversion
*/
public static <Type extends Comparable> int countMerge(Type element[])...{
tot=0;
mergeSort(element.clone());
return tot;
}
/** *//**
* Count the inversion number by O(n^2)
* @param <Type> inversion number type
* @param element inversion number list
* @return count number of inversion
*/
public static <Type extends Comparable> int countInsertion(Type element[])...{
tot=0;
insertionSort(element.clone());
return tot;
}
/** *//**
* @param args
*/
public static void main(String[] args) ...{
Integer[] a = ...{4,6,5,1,3,2,7};
System.out.println(Inversion.countMerge(a));
}
}
最后让我们牢记:
摘《李开复:算法的力量》算法并不局限于计算机和网络
举一个计算机领域外的例子:在高能物理研究方面,很多实验每秒钟都能几个TB的数据量。但因为处理能力和存储能力的不足,科学家不得不把绝大部分未经处理 的数据丢弃掉。可大家要知道,新元素的信息很有可能就藏在我们来不及处理的数据里面。同样的,在其他任何领域里,算法可以改变人类的生活。例如人类基因的 研究,就可能因为算法而发明新的医疗方式。在国家安全领域,有效的算法可能避免下一个911的发生。在气象方面,算法可以更好地预测未来天灾的发生,以拯 救生命。
所以,如果你把计算机的发展放到应用和数据飞速增长的大环境下,你一定会发现;算法的重要性不是在日益减小,而是在日益加强。
谢谢大家。
参考书籍
《Inroduction to Algorithms》Thomas H.Cormen,Charles E.Leiserson,Ronald L. Rivest,Clifford Stein.机械工业出版社
《数据结构》殷人昆,陶永雷,谢若阳,盛绚华 清华大学出版社
《算法艺术与信息学竞赛》刘汝佳,黄亮 清华大学出版社
《李开复:算法的力量》http://www.ieee.org.cn/dispbbs.asp?boardID=60&ID=31651
算法初探 之 排序算法相关推荐
- 数据结构之排序算法:内部排序算法的应用与比较
排序算法:内部排序算法的应用与比较 思维导图: 比较: 应用: 思维导图: 比较: 应用:
- 冒泡排序算法和选择排序算法比较
冒泡排序算法详细内容见→冒泡排序算法. 选择排序算法详细内容见→选择排序算法. 冒泡排序算法和选择排序算法的区别: 冒泡排序是比较相邻位置的两个数:而选择排序是按顺序比较,找出最大值或者最 ...
- 算法基础:排序算法之冒泡排序
算法基础:排序算法之冒泡排序 实现:数列有序排序 思想:已知一个数列,令数列中相邻的两个元素一一做比较,按照小大的顺序(或从大到小的顺序),如果前一个数比后一个数大(或后一个数比前一个数大),则互换( ...
- 伍六七带你学算法 进阶篇-排序算法
给定一个整数数组 nums,将该数组升序排列. 示例 1: 输入:[5,2,3,1] 输出:[1,2,3,5] 示例 2: 输入:[5,1,1,2,0,0] 输出:[0,0,1,1,2,5] 各排序算 ...
- python常见的排序算法_常见排序算法之python实现
1. 冒泡排序 时间复杂度为O(n^2), 稳定的排序算法 思路:一开始比较的区间是[0,n-1],依次比较相邻两数,哪个数大哪个数就放在后面,这样一次遍历数组后,最大的数会在数组的最后一个位置,然后 ...
- ef 排序string转int_排序算法之基本排序算法
基本排序算法 选择排序 选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未 ...
- 【每日算法】桶排序算法
1)算法简介 桶排序 (Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶子里.每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序 ...
- java8 lambda 排序算法,Java8中排序算法比较器的三种写法(使用lambda表达式实现Comparator比较器)...
在涉及到数组, 集合等这些地方经常会需要用到排序算法, 在Java中的Collections类中有sort方法, 除了需要传入一个Comparator比较器, 或者需要排序的类实现了Comparabl ...
- java排序算法总结_排序算法总结及Java实现
1. 整体介绍 分类 排序大的分类可以分为两种,内排序和外排序.在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序.主要需要理解的都是内排序算法: 内排序可以分为 ...
最新文章
- 推荐两个非常实用的,Python装饰器
- Spring集合 (List,Set,Map,Properties) 实例
- pat根据中序遍历和先序遍历_算法题399:从前序与中序遍历序列构造二叉树
- 【推荐】Nginx基础知识之————多模块(非覆盖安装、RTMP在线人数实例安装测试)
- Progressive Web App(PWA)
- elasticsearch和php,快速开始 | Elasticsearch-PHP | Elastic
- SVN使用import导入新数据到版本库
- 如何评判在线直播源码优劣?视频直播软件开发经验之谈
- 第一次作业:基于Linux进程模型分析
- opencv BRIEF角检测
- 科来网络分析系统与数据包分析
- python 基础代谢率计算_Python入门案例(三):BMR(基础代谢率)计算器
- monk_notebook (交际德语教程 第二版 学生用书)
- 英伟达发布3款RTX 20系列游戏显卡,性能提升6倍让游戏更像电影
- 【RegNet】《Designing Network Design Spaces》
- 【C++】面向对象之继承篇
- android tf 读写,Android对于外置TF卡的读写操作(权限获取)
- 绿色免费企业管理软件V3.2┊财务、进销存、生产、人事管理、工资管理、考勤管理...
- codeforces 1569 D. Inconvenient Pairs
- STM32使用LORA模块通信
热门文章
- Git生成生成公钥和私钥
- JAVA8的ConcurrentHashMap为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何 设计。
- 新浪微博Android客户端开发之OAuth认证篇
- 中式客厅装修的特点 亦古亦今的惊艳每一家
- 如何记账能简单高效,记账全攻略来了
- Android 获取手机的 IMEI 值 (设备标识码)
- 国外问卷调查好做吗?为大家分享干货!
- 支持生僻字且自动识别utf-8编码的php汉字转拼音类,支持生僻字且自动识别utf-8编码的php汉字转拼音类_PHP教程...
- 科大奥瑞物理实验——傅里叶光学
- java script中extends,JavaScript继承之ES6的extends