说明: 我自己在学习数据结构时,是从代码中分析,边分析边学习。
1 查找 (常用的是:线性查找和二分查找)
查找: 在一组项中找到指定的目标元素或者判定组内存不存在目标元素的过程 。 要查找的 数据组项我们一般成为查找池。
我们首先给出查找的java代码:
Contact.java – 每个Contact对象表示一个人,包含 姓、名、电话等属性。
public class Contact implements Comparable{
private String firstName, lastName, phone;
public Contact(String first, String last, String telephone){ //构造方法
firstName = first;
lastName = last;
phone = telephone;
}
public String toString(){ //重写toString方法,按设置的要求输出
return lastName + "," + firstName + ":" + phone;
}
public int compareto(Object other){ // 实现方法,
int result;
if (lastName.equals(((Contact)other).lastName))
result = firstName.compareto(((Contact)other).firstName);
else
result = lastName.compareto(((Contact)other).lastName);
return result;
}
}
注
为了能够找到目标,我们必须能对对象进行比较。 我们通过实现Comparable接口,来实现这些算法。 在Comparable接口中包含一个compareTo方法:如果对象分别小于、等于、大于所被比较的对象时,则方法返回小于0、等于0、大于0的一个整数。所以 实现该接口的类中,要定义类中任意两个对象之间的相互次序。(通过实现compareTo方法) 注:实现一个和方法和重写一个方法的区别: 实现: 父类没有做,子类做了 。重写:父类做了,子类和父类做的不一样。
SearchPlayerList.java
public class SearchPlayerList{
public static void main(){
Contact[] players = new Contact[5]; // 创建Contact对象数组
players[0] = new Contact("Rodger","Federer","610-555-7384");
players[1] = new Contact("Andy","Roddick","215-555-3827");
players[2] = new Contact("Maria","Sharapova","733-55-2969");
players[3] = new Contact("Venus","Williams","663-555-3985");
players[4] = new Contact("Lleyen","Hewitt","464-555-3489");
Contact target = new Contact("Venus","Williams","663-555-3985"); // 创建要比较的对象
Contact found = (contact)Searching.linearSearch(players, target); // 调用Searching类中的静态linearSearch方法查找一个具体的Contact对象。注意返回的是Contact对象
if(found == null){
System.out.println("player was not found");
}
else
System.out.println("Found" + found);
}
}
searching.java — 算法的核心
public class searching{ // 创建searching类,里面包含两个静态方法 线性查找和二分查找
public static Comparable linearSearch(Comparable[] data, Comparable target){ // 返回和输入参数类型都是Comparable对象,多态的体现
Comparable result = null; // 初值null 若返回null,代表没找到目标
int index = 0; // 数组中的下标索引
while(result==null && index < data.length){
if (data[index].compareTo(target)==0){
result = data[index];
}
index++; //逐个进行查找
}
return result;
}
public static Comparable binarySearch(Comparable[] data, Comparable target){
Comparable result = null;
int first = 0, last = data.length-1, mid; //位置索引
while(result==null && first<=last){
mid = (first+last)/2; //中间位置索引
if (data[mid].compareTo(target)==0)
result = data[mid]; //中间位置元素与目标相同,返回
else
if(data[mid].comapreTo(target)<0) //判断目标在中间位置的左边还是右边
last = mid-1;
else
first = mid+1;
}
return result;
}
}
理论
线性查找: 从表头开始,逐个寻找对比。算法容易理解, 但是效率不高。
二分查找: 在每次比较时;,借助于查找池中数据的有序性,从而避免大量的比较。二分查找,一开始是从表的中间开始,如果表的中间位置元素不是要找的目标,则继续查找。因为表是有序的,可知,如果target在表中的话,根据目标元素与中间元素的大小关系,它一定在表的左半边或着右半边。
该算法的任意一趟查找中, 候选区间的边界都是有整型变量first和last来表示的。初始值设置为整个数组区间。 一般地,二分法查找 ,效率高。
2 排序
常用的排序: 选择排序、插入排序、冒泡排序、快速排序、归并排序。
前三个效率差不多,但方法不同,后面两个的效率更高,但是算法更复杂。
SortPlayerList.java – 创建一个数组对象,赋值后,调用方法对元素进行排序。
public class SortPlayList{
public static void main(){
Contact[] players = new Contact[5]; // 创建Contact对象数组
players[0] = new Contact("Rodger","Federer","610-555-7384");
players[1] = new Contact("Andy","Roddick","215-555-3827");
players[2] = new Contact("Maria","Sharapova","733-55-2969");
players[3] = new Contact("Venus","Williams","663-555-3985");
players[4] = new Contact("Lleyen","Hewitt","464-555-3489");
Sorting.selectionSort(players); // 选择排序
Sorting.insertionSort(players); // 插入排序
Sorting.bubbleSort(players); // 冒泡排序
Sorting.quickSort(players); // 快速排序
Sorting.mergeSort(players); // 归并排序
for (Comparable player : players)
System.out.println(player);
}
}
Sorting – 排序算法核心代码
public class Sorting{
// 选择排序
public static void selectionSort(Comparable[] data){
int min;
for (int index=0;index<data.length-1;index++){//外层循环控制数组重要存储的下一个最小值的位置(遍历整个数组)
min = index; // 每次内层循环结束,就要除去已经排序好的元素。
for(int scan=index+1;scan<data.length;scan++){ // 内层循环在剩余数组里面找最小值。
if(data[scan].compareTo(data[min])<0) //找到比min位置的小值
min = scan; // 最小值位置索引赋值给min
} //内for
swap(data, min,index); // 调用方法交换数组中两个元素
}//外for
}
public static void swap(Comparable[] data, int index1, int index2){
Comparable temp = data[index1];
data[index1] = data[index2];
data[index1] = temp;
}
// 插入排序
public static void insertionsort(Comparable[] data){
for (int index=1; index<data.length;index++){ //从1开始,是因为第一个元素有序,不需要移动任何元素
Comparable key = data[index]; // 从查找池中逐个取出元素,准备往子序列中插入
int position = index; // 从后往前来比较
while(position>0 && data[posion-1].compareTo(key)>0){ //如果position-1位置的元素比要插入的值大,则position-1的元素右移
data[position] = data[position-1]; //右移
position--; // position减1 往前继续比较
}
data[position] = key; //将比较好顺序的值key插入到 position位置, 此时key值比position-1位置的元素值大。
}
}
// 冒泡排序 (从后往前 冒泡)
public static void bubbleSort(Comparable[] data){
for(int position=data.length-1;position>=0;position--){ //外循环循环n-1次。从后往前
for(int scan=0;scan<=position-1;scan++){ //内循环比较相邻两个数,注意外循环使内循环中最大元素的下标位置减1
if(data[scan].compareTo(data[scan+1])>0) //邻近两个数 前比后大
swap(data,scan,scan+1); //这里调用之前的swap方法实现交换两个数
}
}
}
//快速排序
public static void quickSort(Comparable[] data, int min, int max){
// min max 用到划分子段的最小 最大下标
int pivot; //存储划分元素的下标
if (min<max){
pivot = partition(data, min, max); //调用方法,划分子段。
quickSort(data, min, pivot-1);
quickSort(data, pivot+1, max); // 递归来对划分元素两边的子段排序。
}
}
public static int partition(Comparable[] data, int min, int max){
// 返回是划分元素索引
Comparable partitionValue = data[min]; //划分元素初始值,一般为第一个元素
int left = min;
int right = max; // 左 右两个下标
while(left<right){
while(data[left].compareTo(data[partitionValue])<=0 && left<right){ // 左边的值小于等于划分元素 继续扫描 直到找到比划分元素大的
left++; // 从左向右扫描
}
while(data[right].compareTo(data[partitionValue])>0){
// 右面的元素大于划分元素 直到找到小于划分元素的
rigth--; // 从右往左扫描
}
if(left<right){
swap(data, left, right); //交换两个元素
}
}
swap(data, min, right); //划分元素从最初位置移到在数组中最终位置。这里 要min和right交换的原因是:在最后排好后,还会在扫描一次,此时left>right 内循环不在执行,但下标已改变。注(**)
return right;
}
//归并排序
public static void mergeSort(Comparable[] data, int min, int max){
if(min<max){
int mid = (min+max)/2; //判定待排序列中间点
mergeSort(data, min, mid); // 两次递归调用
mergeSort(data, mid+1, max);
merge(data, min, max); //最后合并两个已有序子段
}
}
public static void merge(Comparable[] data, int first, int mid, int last){
Comparable temp = new Comparable[data.length];
int first1 = first, last1 = mid; //子表1
int first2 = mid+1; last2 = last; //子表2
int index = first1;
while(first1<=last1 && first2<=last2){ //循环判定
if (data[first1].compareTo(data[first2])<0){ //将两个子表最小值放入临时变量
temp[index] = data[first1];
first1++;
}
else{
temp[index] = data[first2];
first2++;
}
index++;
}
while(first1<=last1){//判定子表1 是否为空
temp[index] = data[first1];
first1++;
index++;
}
while(first2<=last2){
temp[index] = data[first2];
first2++;
index++;
}
for(index=first;index<=last;index++){
data[index] = temp[index];
}
}
}
理论
(1) 选择排序
一般策略: 扫描整个表,找到最小值,将这个最小值与表中的第一个位置的值想交换。扫描余下的表,查找最小值,将这个值于表的第二个位置的值交换。不断重复该过程。最终完成排序。
(2) 插入排序
一般策略:对表中最前面的两个元素的值进行排序,必要的话就进行交换。将表中的第三个值插入到前面两个(已有序)值组成的子序列中的合适位置。不断重复地将一个特定的值插入到表中已有序的子序列中,从而完成一组值的排序。
(3)冒泡排序
一般策略: 扫描整个表,重复地比较相邻元素,如果呈逆序,则交换它们。执行结果是最大值方到了表的最后位置。 每趟扫描都能至少将一个值放到正确的位置。所以整个过程只需要执行(n-1)次。
#### 以上三种算法都是双循环, 对n个元素排序,需要进行n^2次比较 ####
(4) 快速排序
一般策略: 先选择表中的一个元素当作划分元素,接下来对表进行划分。小于划分元素的所有元素放到划分元素的左侧,大于划分元素的放到其右侧。最后,再用这个快速排序的策略(递归的)对两个划分子段进行排序。
初始时,如果要排序的项是随机分配的,一般选取第一个元素担当划分元素。
注()**
(5) 归并排序
归并排序:递归的对分表,直到每个子段只含一个元素时为止,然后再将子表按序合并,从而完成对表的排序。
一般测略: 开始时将表划分为大致相等的两段,然后对每个子表递归调用自身。