版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
排序算法的稳定性
冒泡,插入,归并可以。
选择,快速,堆。不行
稳定性的作用:实际案列:如果按照分数进行排序,然后在按照班级排序,那么稳定性的体现就是:在同一个班级中从上到下的一定是按照分数排好序的。
例题1:
思路:因为不能使用非基于比较的排序,所以不能应用桶排序后遍历找最大差值。但可以应用桶排序的思想:创建一个比原数组个数加一的桶,数组的最小值放在第0号桶里,数组的最大值放在最后一个桶里,然后遍历数组,桶内只放该区域的最大值和最小值。遍历之后中间一定会有一个空桶,要注意的是空桶的左边的最小值-右边的最大值并非为最大差值,设置空桶的目的是为了排除同一桶内存在最大差值的问题。
注意:对于初始值max和min的初始值的大小。巧利用。
public static int maxGap(int[] arr){
if (arr == null || arr.length < 2){
return 0;
}
int length = arr.length;
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for (int i = 0; i < length; i++) {
max = Math.max(max,arr[i]);
min = Math.min(min,arr[i]);
}
// 如果数组中只有一个数
if (max == min){
return 0;
}
boolean[] hasNum = new boolean[length + 1];
int[] maxs = new int[length + 1];
int[] mins = new int[length + 1];
int bid = 0;// 表示数组中的当前元素应该进入哪个桶(打标记)
// 更新桶中的最大值和最小值和是否进入过数
for (int i = 0; i < length; i++) {
bid = bucket(arr[i],length,min,max);
mins[bid] = hasNum[bid] ? Math.min(arr[i],mins[bid]) : arr[i];
maxs[bid] = hasNum[bid] ? Math.min(arr[i],maxs[bid]) : arr[i];
hasNum[bid] = true;
}
// 遍历每一个桶,比较当前非空桶的最小值和上一个非空桶的最大值之间的差值和全局差值的大小关系
int result = 0;
int lastMax = maxs[0];
for (int i = 1; i < maxs.length; i++) {
if (hasNum[i]){
result = Math.max(result, mins[i] - lastMax);
lastMax = maxs[i];
}
}
return result;
}
/**
* 确定num的桶编号
* @param num
* @param length
* @param min
* @param max
* @return
*/
private static int bucket(int num, int length, int min, int max) {
return((num - min) * length / (max - min));
}
例题2:用数组结构实现大小固定的队列和栈。
注意:Array.copyOf(int[] original, int newLength); //第一个参数是原始数组,第二个是新数组的长度。
/**
* 使用固定数组容量实现一个栈结构。每次入栈之前先判断栈的容量是否够用,如果不够用就用
* Arrays.copyOf()进行扩容
*
* @author fatah
*
*/
public class MyStack {
private int[] storage;//存放栈中元素的数组
private int capacity;//栈的容量
private int count;//栈中元素数量
private static final int GROW_FACTOR = 2;
//TODO:不带初始容量的构造方法。默认容量为8
public MyStack() {
this.capacity = 8;
this.storage=new int[8];
this.count = 0;
}
//TODO:带初始容量的构造方法
public MyStack(int initialCapacity) {
if (initialCapacity < 1)
throw new IllegalArgumentException("Capacity too small.");
this.capacity = initialCapacity;
this.storage = new int[initialCapacity];
this.count = 0;
}
//TODO:入栈
public void push(int value) {
if (count == capacity) {
ensureCapacity();
}
storage[count++] = value;
}
//TODO:确保容量大小
private void ensureCapacity() {
int newCapacity = capacity * GROW_FACTOR;
storage = Arrays.copyOf(storage, newCapacity);
capacity = newCapacity;
}
//TODO:返回栈顶元素并出栈
private int pop() {
count--;
if (count == -1)
throw new IllegalArgumentException("Stack is empty.");
return storage[count];
}
//TODO:返回栈顶元素不出栈
private int peek() {
if (count == 0){
throw new IllegalArgumentException("Stack is empty.");
}else {
return storage[count-1];
}
}
//TODO:判断栈是否为空
private boolean isEmpty() {
return count == 0;
}
//TODO:返回栈中元素的个数
private int size() {
return count;
}
}
固定数组实现队列:用size来判断:size没满之前都可以加元素,size为0之前都可以取元素,end:表示加入的指针,start表示出队列的指针。
package cn.itcats.array;
/**
* 使用固定数组长度实现一个有界队列
* @author fatah
*
*/
public class ArrayQueue {
private Integer start;
private Integer end;
private Integer[] arr;
private Integer size;
/**
* @param initSize 初始化有界队列大小
* @throws
*/
public ArrayQueue(int initSize) {
if(initSize < 0) {
throw new IllegalArgumentException("初始化队列失败,initSize: "+initSize);
}
this.end = 0;
this.start = 0;
this.size = 0;
arr = new Integer[initSize];
}
/**
* 入队
* @param value 传入加入队列的值
* @throws ArrayIndexOutOfBoundsException 队列已满
*/
public void push(int value) {
if(arr.length == size) {
throw new ArrayIndexOutOfBoundsException("队列已满");
}
size ++;
arr[end] = value;
end = end < arr.length - 1 ? end +1 : 0;
}
/**
* 出队
* @throws ArrayIndexOutOfBoundsException 队列已空
*/
public Integer pop() {
if(size == 0) {
throw new ArrayIndexOutOfBoundsException("队列已空");
}
size --;
int temp = start;
start = start < arr.length -1 ? start + 1 : 0;
return arr[temp];
}
/**
* 返回队列的第一个元素
*/
public Integer peek() {
if(size == 0) {
return null;
}
return arr[start];
}
}
例3:
思路:
1.为保证操作时间复杂度为O(1) 需要准备两个栈:data栈,min栈
2.栈空入栈的时候,data栈和min栈都需要压入这个数,栈不空时,data栈正常入栈,入min栈的时候,拿当前数与min栈栈顶的数比较,较小的数入min栈
3.弹栈的时候,data栈和min栈同时弹出栈顶数据
4.通过getMin获取最小值的时候,直接返回min栈的栈顶数但是不弹出
public class Code_02_GetMinStack {
/**
* 算法设计思路:
* 1、初始化两个栈,一个数据栈,一个用作存储当前所有元素最小值的栈
* 2、数据栈每压入一个元素,更新Min栈,保证Min栈栈顶始终为当前Data栈中的最小值
* 3、Min栈的好处是,当最小值弹出时,Min栈相应也弹出,
* 而此时Min栈栈顶也是弹出后的数据栈的最小值,也保证了时间复杂度O(1)
*/
public Stack<Integer> stackData;
public Stack<Integer> stackMin;
public Code_02_GetMinStack(){
this.stackData=new Stack<Integer>();
this.stackMin =new Stack<Integer>();
}
/**
* @Description: 压栈操作 当栈空或者待压入数字小于Min栈栈顶时 先压入Min
* @param: 待压入数字
* @date: 2019/4/23 15:52
*/
public void push(int newNum){
if (stackMin.isEmpty()||newNum <= getMin()){
stackMin.push(newNum);
}
stackData.push(newNum);
}
/**
* @Description: 弹栈操作 :
* 弹出data栈栈顶,若该数据与Min栈栈顶相等 则Min也做弹栈操作
* 保证Min栈栈顶始终为栈内最小元素
* @return: 返回data栈栈顶
* @date: 2019/4/23 15:59
* @throws 当空栈时 抛出数组越界异常
*/
public Integer pop(){
if (stackData.isEmpty())
throw new ArrayIndexOutOfBoundsException("栈空");
int temp=stackData.pop();
if (temp==getMin())
stackMin.pop();
return temp;
}
/**
* @Description: 获取栈中最小元素 时间复杂度O(1)
* @date: 2019/4/23 15:53
* @throws
*/
public Integer getMin(){
if (stackMin.isEmpty())
throw new ArrayIndexOutOfBoundsException("栈空!");
return stackMin.peek();
}
例题:
思路:
- 栈实现队列
思路: 入数据。②向Pop栈倒入数据时必须一次就要倒完。
固定栈1入队,栈2出队。pop() 操作时,①如果两栈都为空,报异常;②如果出队栈有元素就出队;③如果出队栈为空,就把入队栈的元素都弹过来再出队。
import java.util.Stack;
public class e04StackToQueue {
private Stack<Integer> s1;
private Stack<Integer> s2;
public e04StackToQueue(){
s1=new Stack<>();
s2=new Stack<>();
}
//入队列操作
public void add(int obj){
s1.push(obj);
}
//出队列操作
public int deQueue(){
if (s1.isEmpty()&&s2.isEmpty()){
throw new RuntimeException("empty");
}
if (s2.isEmpty()){ //必须要这个队列为空才行。
while (!s1.isEmpty()){
s2.push(s1.pop());
}
}
return s2.pop();
}
}
- 队列实现栈
图的深度优先遍历需要用栈来实现。
用队列实现图的深度优先遍历方法是:先用队列实现栈结构,再用栈结构实现图的深度优先遍历。
代码思路:构建两个队列:Data队列和help队列;压入数据时数据都进Data队列,假设队列中按顺序进入了1,2,3,4,5,返回数据时,把1,2,3,4 放入help队列,然后拿出Data中的5返回;接着改引用,将Data队列和help队列的引用交换;下次返回数据时就依然是把Data队列中的123放入help队列,然后拿出Data队列中的4返回,再交换两队列的引用,如此反复。
import java.util.LinkedList;
import java.util.Queue;
public class e05QueueToStack {
private Queue<Integer> queue;
private Queue<Integer> help;
public e05QueueToStack(){
queue=new LinkedList<>();
help=new LinkedList<>();
}
public void push(Integer e){
queue.add(e);
}
public int pop(){
if (queue.isEmpty()){
throw new RuntimeException("empty");
}
while (queue.size()!=1){
help.add(queue.poll());
}
int res=queue.poll();
swap();
return res;
}
public int peek(){
if (queue.isEmpty()){
throw new RuntimeException("empty");
}
while (queue.size()>1){
help.add(queue.poll());
}
int res=queue.poll();
help.add(res);
swap();
return res;
}
//这里就只需要换一下引用。
public void swap(){
Queue<Integer> tmp=queue;
queue=help;
help=tmp;
}
}