对象的比较
玫瑰都是在荆棘中绽放的!
比较引用类型的关系:
- 大小关系:
实现比较器Comparable(compareTo方法重写:调用的对象是this) Comparator接口(直接写一个类来实现该接口,其中要重写compare方法) - 相等关系:true false
== 比较值(地址 or 直接值) 否则就需要重写equals方法,equals方法的底层默认还是 ==
努力成为你想成为的人!
一、排序的概念及应用
1. 排序的概念
- 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
- 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳
定的;否则称为不稳定的。
(稳定性:就是指如果存在多个相同的关键字,如果排序前后其相对位置一样就说明具有稳定性。)
- 内部排序:数据元素全部放在内存中的排序。(也就是相当于在RAM中排序)
- 外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。(也就是相当于在磁盘中排序)
- 思考:Arrays.sort -->底层是一个什么排序?
- 注:在本章节讲到的所有排序都默认是从小到大排列,所讲的都是内部排序。
2. 常见的排序算法
二、常见排序算法的实现
1. 插入排序(用处广泛)
1. 基本思想
直接插入排序是一种简单的插入排序法,其基本思想是: 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。实际中我们玩扑克牌时,就用了插入排序的思想。
2. 直接插入排序
- 当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。(即:比较 插入)
- 直接插入排序的特性总结:
1) 元素集合越接近有序,直接插入排序算法的时间效率越高 2) 时间复杂度:O(N^2) 3) 空间复杂度:O(1),它是一种稳定的排序算法 4) 稳定性:稳定
- 代码思路:从第二个元素开始排序:设i,j=i-1,然后进行比较,i++,j–(可能会出现j<0),然后又j=i-1。(需要一个临时变量tmp)
(其实也就是:第n个数据要与前n-1个比较) - 源码:
public void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int tmp = arr[i];
int j = i-1;
for (; j >= 0; j--) {
if(tmp<arr[j]) {
arr[j+1] = arr[j];
} else {
break;
}
}
arr[j+1] = tmp;
}
}
3. 希尔排序(缩小增量排序)
- 希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成若干个组,所有相隔某个“增量”的记录分在同一内,并对每一组内的记录进行排序。然后,重复上述分组和排序的工作。当到达==1时,所有记录在统一组内排好序。
- 简而言之,希尔排序shellSort:缩小增量算法:
分组、组内直接插入排序 - 希尔排序其实就相当于跳着排序:使得数据大的尽可能靠后!数据逐渐趋于有序
n组:可以理解为:每两个数据之间间隔n个数据 - 希尔排序:选择增量值(也就是组数),最后一个增量值是1。
希尔排序较少考。 - 希尔排序的特性总结:
1) 希尔排序是对直接插入排序的优化。 2) 当gap(分组,即:增量值) > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。故就整体而言,可以达到优化的效果。 3) 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在许些树中给出的希尔排序的时间复杂度都不固定。 (注:因为我们的gap是按照Knuth提出的方式取值的,而且Knuth进行了大量的试验统计,我们计算时间复杂度暂时就按照:O(n1.25 ) ~ O(1.6* n1.25)来计算 4)稳定性:不稳定
- 源码:
private static void shell(int[] arr, int gap) {
for (int i = gap; i < arr.length; i++) {
int tmp = arr[i];
int j = i-gap;
for (; j >= 0; j-=gap) {
if(tmp<arr[j]) {
arr[j+gap] = arr[j];
} else {
break;
}
}
arr[j+gap] = tmp;
}
}
public static void shellSort(int[] arr) {
int gap = arr.length;
while(gap > 1) {
shell(arr,gap);
gap /= 2;
}
shell(arr,1);
}
2. 选择排序
1. 基本思想
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
2. 直接选择排序
- 代码思路:
定义i,j下标,j下标移动寻找比i下标的元素小的值并记录其下标minIndex,继续移动更新minIndex;当i走完后,minIndex所指的值与i下标所指的值进行交换,然后i++;j=i+1,minIndex=i;重复以上操作。(minIndex的初值记录为当前i下标对应的值)
- 【直接选择排序的特性总结】:
1)直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用 2) 时间复杂度:O(N2) 3) 空间复杂度:O(1) 4) 稳定性:不稳定
- 直接选择排序源码:
public static void selectSort(int[] arr) {
int len = arr.length;
for (int i = 0; i < len-1; i++) {
int minIndex = i;
for (int j = i+1; j < len; j++) {
if(arr[minIndex] > arr[j]) {
minIndex = j;
}
}
swap(arr,minIndex,i);
}
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
- 直接选择排序的优化:
直接选择排序的优化,同时选择两个元素,一个最小放左边,一个最大放右边。注意:修正maxIndex!!
- 直接选择排序优化的源码(时间复杂度不变):
public static void selectSort2(int[] arr) {
int left = 0;
int right = arr.length-1;
while(left<right) {
int minIndex = left;
int maxIndex = left;
for (int i = left+1; i <= right; i++) {
if(arr[i]<arr[minIndex]) {
minIndex = i;
}
if(arr[i]>arr[maxIndex]) {
maxIndex = i;
}
}
swap(arr,left,minIndex);
if(left == maxIndex) {
maxIndex = minIndex;
}
swap(arr,right,maxIndex);
left++;
right--;
}
}
3. 堆排序
- 堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。(即:从小到大建大根堆)
- 从小到大(升序):建立大根堆(需要使用【向下调整】)、每次最后一个元素跟堆顶元素进行交换(注意:最后一个元素下标是会逐步向前的)、交换后【向下调整】保证大根堆。
- 【直接选择排序的特性总结】:
1) 堆排序使用堆来选数,效率就高了很多。 2) 时间复杂度:O(N*logN) 3) 空间复杂度:O(1) 4) 稳定性:不稳定
- 源代码:
public static void heapSort(int[] arr) {
createBigHeap(arr);
int end = arr.length-1;
while(end>=0) {
swap(arr,0,end);
shiftDown(arr,0,end);
end--;
}
}
private static void createBigHeap(int[] arr) {
for (int parent = (arr.length-1-1)/2; parent >= 0; parent--) {
shiftDown(arr,parent,arr.length);
}
}
private static void shiftDown(int[] arr,int parent, int len) {
int child = 2*parent+1;
while(child<len) {
if(child+1<len && arr[child]<arr[child+1]) {
child++;
}
if(arr[child] > arr[parent]) {
swap(arr,child,parent);
parent = child;
child = 2*parent+1;
} else {
break;
}
}
}
THINK
- 插入排序:直插、希尔(缩小增量法)
- 选择排序:直接选择、堆排
- 注意时间以及空间复杂度
- 注意代码思路以及书写
- 稳定性
|