基础回顾:
【1】冒泡排序:从头开始,两两相近比较,小的往前冒泡
【2】选择排序:从头开始,每一轮选出最小的放到最前面
【3】插入排序:默认头部是排好序的,每次从头部下一个往前冒泡插入
组合:前提条件——几乎已经排好序的数据等等
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
- 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
- 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
【1】希尔排序:通过增量分段使用插入排序,有一限制:最终的分段增量一定为1——这也增加其复杂度!
public static void ShellSort(int[] data){
int g = 1; //保障最终增量为1
while(g<data.length){
g = g*3+1;//生产增量
}
while(g>0){
for(int i = g;i<data.length;i++){
int j = i - g;
int temp = data[i];//下面就是插入排序
while(j>=0 && data[j]>temp){
data[j+g] = data[j];
j -= g;
}
data[j+g] = temp;
}
g = g/3;
}
System.out.println(Arrays.toString(data));
}
归并排序——分治法【递归】
创建在归并操作上的一种有效的排序算法,效率为O(n log n)。1945年由约翰·冯·诺伊曼首次提出。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。
对递归的真正认知:明白递归停地时候,关键节点的操作!——对于分治两两进行拼接排序——递归到底:两两数字,然后两两组合的归并
public static void sort(int[] data,int left,int right,int[] temp){
if (left<right) {
int mid = (left + right)/2;
sort(data, left, mid, temp);
sort(data, mid+1, right, temp);
//对于分治两两进行拼接排序——递归到底:两两数字,然后两两组合的归并
merge(data, left, mid, right, temp);
}
}
public static void merge(int[] data,int left,int mid ,int right,int[] temp){
int i = left;
int j = mid+1;
int t = 0;
while(i<=mid && j<=right){
if (data[i]<data[j]) {
temp[t++] = data[i++];
}else {
temp[t++] = data[j++];
}
}
while(i<=mid){
temp[t++] = data[i++];
}
while(j<=right){
temp[t++] = data[j++];
}
t=0;
while (left<=right) {
data[left++] = temp[t++];
}
}============================================
改进1:采用两个线程:???
【1】线程之间共享数据data,一个负责拆分,一个负责合并
快速排序:分治+冒泡
核心思想:
-先对左右两端以及中心的三个元素排序,将中值作为基准值;
-将基准值放到倒数第二位,开始将基准值之前的数据中大于基准值的放到靠近基准值一侧,然后将基准值与其左侧大于其值的第一个交换,将整个数据划分为左右两个区域
-继续分别对左右两个区域进行递归分治——结素标志:两个数值冒泡交换位置
-
public static void sort(int[] data,int left,int right){
if (left<right) {
dealPivot(data, left, right);
int mid = (left+right)/2;
swap(data,mid ,right-1);
int i = left;
int j = right-1;
while (true) {
while (data[++i]<data[right-1]) {}
while(j>i && data[--j]>data[right-1]){}
swap(data, i, j);
if (i>=j) {
break;
}
}
swap(data, i, right-1);
sort(data, left, i-1);
sort(data, i+1, right);
}
}
public static void dealPivot(int[] data,int left,int right){
int mid = (left+right)/2;
if (data[left] > data[mid]) {
swap(data, left, mid);
}
if (data[left] > data[right]) {
swap(data, left, right);
}
if (data[mid] > data[right]) {
swap(data, mid, right);
}
}
public static void swap(int[] data,int left,int right){
int temp = data[left];
data[left] = data[right];
data[right] = temp;
}
堆排序——堆结构、完全二叉树:交换堆顶与堆尾;缩小堆的尺寸
【1】大顶堆:
-is[i] >= is[2i+1]
-is[i] >= is[2i+2]
【2】小顶堆
-is[i] <= is[2i+1]
-is[i] <= is[2i+2]
===========================
【1】找第一个非叶子节点: data.length/2 - 1
=====逻辑思路到代码的转换又是一个过程!!!
public static void sort(int[] data){
//先进行一次全部非叶子节点的大顶堆调整——由下至上,由右往左
for(int i=data.length/2-1;i>=0;i--){
injust(data, i, data.length);
}
//整体交换一次堆顶最大元素放置末尾,做一次大顶堆调整——由上至下,由左往右
for(int i=data.length-1;i>0;i--){
swap(data, 0, i);
injust(data, 0, i);
}
}
public static void injust(int[]data,int i,int length){
int temp = data[i];
//解决该小堆排序问题,以及由于堆顶调整导致下面节点的重新调整
for(int k=i*2+1;k<length;k=k*2+1){
if (k<length-1 && data[k]<data[k+1]) {
k++;
}
if (data[k]>temp) {
data[i] = data[k];
i = k;
}else {
break;//说明后续节点没有受到影响,结束
}
}
data[i] = temp;
}
public static void swap(int[] data,int left,int right){
int temp = data[left];
data[left] = data[right];
data[right] = temp;
}
==============计数排序:有一定的前置条件:在特定条件下,比以上7种都要快
【1】计数排序:固定范围的正整数放入到额外开辟的空间【最好均匀分布满足一定的统计分布】
-核心思想:通过一个区段计数器:游标=源数组数据,value:源数组数据出现频次
public static void sort(int[] data){
int max = data[0];
for(int i=0;i<data.length;i++){
if (data[i]>max) {
max = data[i];
}
}
int[] temp = new int[max+1];
for(int i=0;i<data.length;i++){
temp[data[i]]++;
}
int index = 0;
for(int i=0;i<temp.length;i++){
for (int j = 0; j < temp[i]; j++) {
data[index++]=i;
}
}
}
桶排序——hashMap && 红黑树
核心思想:通过自定义桶的个数,通过最大最小值划定跨度,采用likedList数据结构进行插入排序,最终合并所有数据。
public static int[] sort(int[] data){
int bucketCount = 3;
int max=data[0];
int min=data[0];
for(int i:data){
if (max<i) {
max = i;
}
if (min>i) {
min = i;
}
}
double space = (max-min+1)/bucketCount;
List<Integer>[] buckets = new LinkedList[bucketCount];
for(int num:data){
int index = (int)(Math.floor((num-min)/space));
if (buckets[index]==null) {
buckets[index] = new LinkedList<Integer>();
buckets[index].add(num);
}else {
for(int i=buckets[index].size()-1;i>=0;i--){
if (num<buckets[index].get(i)) {
if (i+1>buckets[index].size()-1) {
buckets[index].add(buckets[index].get(i));
if (i==0) {
buckets[index].set(i, num);
}
}else {
buckets[index].set(i+1, buckets[index].get(i));
}
}else {
if (i+1>buckets[index].size()-1) {
buckets[index].add(num);
i++;
break;
}else {
buckets[index].set(i+1, num);
break;
}
}
}
}
}
int index = 0;
for(int i=0;i<=buckets.length-1;i++){
for(int j=0;j<=buckets[i].size()-1;j++){
data[index++] = buckets[i].get(j);
}
}
return data;
}
基数排序:对个、十、百、千…位依次进行排序
基数排序(英语:Radix sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。基数排序的发明可以追溯到1887年赫尔曼·何乐礼在打孔卡片制表机(Tabulation Machine)上的贡献。
public static void sort(int[] data,int d){
int n = 1;
int length = data.length;
//本质桶一维数组+链表
while(n<d){
int[][] buckets = new int[10][data.length]; //桶:一维对应桶的位序,二维对应桶内个数,每一位值位存储的实际值
int[] order = new int[10];
for(int num:data){
int cp = num/n%10;
buckets[cp][order[cp]]=num;
order[cp]++;
}
int index = 0;
for(int i=0;i<order.length;i++){
for(int j=0;j<order[i];j++){
data[index++] = buckets[i][j];
}
}
n *= 10;
}
}