算法-数组、栈、队列

一、用数组结构实现大小固定的队列和栈

1、数组实现栈

package FaceQuestion;

/**
 * 使用固定长度的数组实现一个栈结构
 */
public class ArrayStack {

    Integer[] arr;
    Integer index;
    public ArrayStack(int arraySize){
        if (arraySize<0){
            throw new IllegalArgumentException("数组初始化错误");
        }

        arr=new Integer[arraySize];
        index=0;
    }

    /**
     * 返回栈顶元素但不弹出
     * @return
     */
    public Integer peek(){
        if (index==0){
            return null;
        }
        return arr[index-1];
    }

    /**
     * 弹出栈顶元素
     * @return
     */
    public Integer pop(){
        if (index==0){
            throw new ArrayIndexOutOfBoundsException("栈越界!!!");
        }
        return arr[--index];
    }

    /**
     * 压栈
     * @param obj
     */
    public void push(int obj){
        if (index==arr.length){
            throw new ArrayIndexOutOfBoundsException("栈越界!!!");
        }

        arr[index++]=obj;
    }
}

2.数组实现队列

package FaceQuestion;

/**
 * 定长数组实现栈
 * 设置标志位,startend
 * end中插入,start处弹出
 * 同时使用size判断数组越界问题,size记录的是数组中元素个数
 * 每插入一个元素,size++,元素放在end处,并使用nextIndex判断end位置,如果到数组边界,那么就置为0
 * 每弹出一个元素,size--,弹出start处元素,并使用nextIndex判断start位置,如果到数组边界,那么置为0 */
public class ArrayQueue {

    private Integer[] arr;
    private Integer size;
    private Integer start;
    private Integer end;

    /**
     * 初始化定长数组
     * @param initSize
     */
    public ArrayQueue(int initSize){
        if (initSize<0){
            throw new IllegalArgumentException("数组初始化失败");
        }

        arr=new Integer[initSize];
        size=0;
        start=0;
        end=0;
    }

    
    public Integer peek(){
        if (size==0){
            return null; 
        }
        return arr[start];
    }

    public void push(int obj){
        if (size==arr.length){
            throw new ArrayIndexOutOfBoundsException("越界");
        }

        size++;
        arr[end]=obj;
        end=nextIndex(arr.length,end);
    }

    public Integer poll(){
        if (size==0){
            throw new ArrayIndexOutOfBoundsException("越界");
        }
        size--;
        int tmp=start;
        start=nextIndex(arr.length,start);
        return arr[tmp];
    }


    /**
     * 获取startend的移动的下一个位置,如果到了边界,那么就置为0
     * @param length
     * @param index
     * @return
     */
    private Integer nextIndex(int length, Integer index) {
        return index==length-1?0:index+1;
    }

}

二、实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作

package FaceQuestion;

import java.util.Stack;

/**
 * 实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。
 * 思路:使用两个栈来实现,一个正常存储元素,一个只存储小元素(每次入栈都比较一下,比栈顶元素小就压入)
 */
public class GetMinStack {

    private Stack<Integer> stackData;
    private Stack<Integer> stackMin;

    private GetMinStack(){
        this.stackData=new Stack<>();
        this.stackMin=new Stack<>();
    }


    private void push(int newNum){
        if (this.stackMin.isEmpty()){
            this.stackMin.push(newNum);
        }else if (newNum<=this.getMin()){
            this.stackMin.push(newNum);
        }
        this.stackData.push(newNum);
    }

    public int pop() {
        if (this.stackData.isEmpty()) {
            throw new RuntimeException("Your stack is empty.");
        }
        int value = this.stackData.pop();
        if (value == this.getMin()) {
            this.stackMin.pop();
        }
        return value;
    }
    private int getMin() {
        if (this.stackMin.isEmpty()){
            throw new RuntimeException("栈溢出");
        }
        return this.stackMin.peek();
    }
}

三、使用队列实现一个栈

package FaceQuestion;


import java.util.LinkedList;
import java.util.Queue;

/**
 * 使用队列完成栈结构
 * 使用两个队列不断的倒进倒出来实现
 * 创建两个队列1,2
 * 入队时正常入其中一个队,弹出时,将队尾元素保留,前面元素压入到另一个队列中
 */
public class TwoQueueStack {

    private Queue<Integer> queue;
    private Queue<Integer> help;

    public TwoQueueStack(){
        queue=new LinkedList<>();
        help=new LinkedList<>();
    }

    public void push(int pushInt){
        queue.add(pushInt);
    }

    public int peek(){
        if (queue.isEmpty()){
            throw new RuntimeException("栈为空");
        }
        while (queue.size()!=1){
            help.add(queue.poll());
        }

        int res=queue.poll();
        help.add(res);
        swap();
        return res ;
    }

    public int poll(){
        if (queue.isEmpty()){
            throw new RuntimeException("栈为空");
        }

        while (queue.size()>1){
            help.add(queue.poll());
        }
        int res=queue.poll();
        swap();
        return res;
    }

    //交换一下helpqueue的引用
    private void swap(){
        Queue<Integer> tmp=queue;
        queue=help;
        help=tmp;
    }
}

四、使用栈实现一个队列

package FaceQuestion;

import java.util.Stack;

/**
 * 使用两个栈实现一个队列
 * 创建好两个栈,一个用于压入给定的元素,当需要弹出元素时,
 * 需要压入栈的元素全部压入到
 * 弹出栈中,由弹出栈弹出元素
 */
public class TwoStackQueue {

    private Stack<Integer> stackPush;//专门用于压入元素的栈
    private Stack<Integer> stackPop;//专门用于弹出的栈

    public TwoStackQueue(){
        stackPop=new Stack<>();
        stackPush=new Stack<>();
    }


    public void push(Integer newValue){
        stackPush.push(newValue);
    }

    public Integer Poll(){
        if (stackPush.empty()&&stackPop.empty()){
            throw new RuntimeException("栈为空");
        }else if (stackPop.empty()){
            while (!stackPush.empty()){
                stackPop.push(stackPush.pop());
            }
        }

        return stackPop.pop();
    }

    public int peek() {
        if (stackPop.empty() && stackPush.empty()) {
            throw new RuntimeException("Queue is empty!");
        } else if (stackPop.empty()) {
            while (!stackPush.empty()) {
                stackPop.push(stackPush.pop());
            }
        }
        return stackPop.peek();
    }
}
五、设计RandomPool结构
设计一种结构,在该结构中有如下三个功能:
insert(key):将某个key加入到该结构,做到不重复加入。
delete(key):将原本在结构中的某个key移除。
getRandom():等概率随机返回结构中的任何一个key。

package FaceQuestion;

import java.util.HashMap;

/**
 * 设计RandomPool结构
 * 设计一种结构,在该结构中有如下三个功能:
 * insert(key):将某个key加入到该结构,做到不重复加入。
 * delete(key):将原本在结构中的某个key移除。
 * getRandom():等概率随机返回结构中的任何一个key *
 *
 * 思路:
 * 使用两个hashmapHashMap<T,Integer> keyIndexMapHashMap<Integer,T> indexKeyMap
 */
public class RandomPool<K> {


    private HashMap<K, Integer> keyIndexMap;
    private HashMap<Integer, K> indexKeyMap;
    private int size;

    public RandomPool() {
        this.keyIndexMap = new HashMap<>();
        this.indexKeyMap = new HashMap<>();
        //标记hashmap中存储的所有记录个数,同时保证HashMapInteger为连续的正整数,我们就可以通过这个size来等概率返回元素
        this.size = 0;
    }

    public void insert(K key){
        if (!this.keyIndexMap.containsKey(key)){//保证不重复添加
            this.keyIndexMap.put(key,this.size);
            this.indexKeyMap.put(this.size++,key);
        }
    }

    /**
     * 删除时,将最后一条记录和要删除的记录交换,然后删除最后一条记录,可以保证下标连续不间断,从而保证等概率返回
     * @param key
     */
    public void delete(K key){
        if (this.keyIndexMap.containsKey(key)){
            int deleteIndex=this.keyIndexMap.get(key);//获取要删除元素的下标
            int lastIndex=--this.size;
            K lastKey=this.indexKeyMap.get(lastIndex);//获取最后一个元素
            this.keyIndexMap.put(lastKey,deleteIndex);//把最后一个元素的下标赋值到最后一个元素的下标
            this.indexKeyMap.put(deleteIndex,lastKey);//把最后一个元素赋值到要删除元素
            this.keyIndexMap.remove(key);//删除key
            this.indexKeyMap.remove(lastIndex);//删除最后一条记录

        }
    }

    /**
     * 
     * 等概率返回一个key
     * @return
     */
    public K getRandom() {
        if (this.size == 0) {
            return null;
        }
        int randomIndex = (int) (Math.random() * this.size);
        return this.indexKeyMap.get(randomIndex);
    }

}

六、转圈打印矩阵

思路:宏观观察,选择左上角和右下角两个点为观察点,每次打印一圈,然后两个点不断向中间逼近;

package FaceQuestion;

/**
 * 打印矩阵问题
 * 转圈,之字形打印
 *
 */
public class PrintMatrixSpiralOrder {

    public void spiralOrderPrint(int[][] matrix){

        int tRow=0;//左上角x坐标
        int tCol=0;//左上角y坐标
        int dRow=matrix.length-1;//右下角x坐标
        int dCol=matrix[0].length-1;//右下角y坐标

        while (tRow<=dRow&&tCol<=dCol){//当还没有到达中点时,不断转圈打印
            printEdge(matrix,tRow++,tCol++,dRow--,dCol--);
        }
    }

    private void printEdge(int[][] matrix, int tRow, int tCol, int dRow, int dCol) {
        if (tRow==dRow){//到了右边界
            for (int i=tCol;i<=dCol;i++){
                System.out.println(matrix[tRow][i]+" ");
            }
        }else if (tCol==dCol){//到了下边界
            for (int i=tRow;i<dRow;i++){
                System.out.println(matrix[i][tCol]+" ");
            }
        }else {
            int curC = tCol;
            int curR = tRow;
            while (curC != dCol) {
                System.out.print(matrix[tRow][curC] + " ");
                curC++;
            }
            while (curR != dRow) {
                System.out.print(matrix[curR][dCol] + " ");
                curR++;
            }
            while (curC != tCol) {
                System.out.print(matrix[dRow][curC] + " ");
                curC--;
            }
            while (curR != tRow) {
                System.out.print(matrix[curR][tCol] + " ");
                curR--;
            }
        }

    }

}

七、“之”字形打印矩阵之类的问题

package FaceQuestion;

/**
 * 之字形打印矩阵
 * 设置三个观察点,t点,r点,end * t点起始点在(0,0)不断向右移动,移动到边界之后,再向下移动
 * r点起始点在(0,0)不断向下移动,移动到边界之后,再向右移动
 * end点一直在右下角
 */
public class ZigPrintMatrix {

    public static void printMatrixZigZag(int[][] matrix) {
        int tR = 0;//不断向右移动点的y坐标
        int tC = 0;//不断向右移动点的x坐标
        int dR = 0;//不断向下移动点的y坐标
        int dC = 0;//不断向下移动点的x坐标
        int endR = matrix.length - 1;
        int endC = matrix[0].length - 1;
        boolean fromUp = false;//判断移动的方向
        while (tR != endR + 1) {//打印结束条件,t点向下移动到越界
            printLevel(matrix, tR, tC, dR, dC, fromUp);//根据方向打印对角线的方法
            //通过不断改变t点和r点的坐标,使得t点和r点处在对角线两端
            tR = tC == endC ? tR + 1 : tR;//t点的y坐标起初为0,到达右边界之后,不断+1
            tC = tC == endC ? tC : tC + 1;//tx坐标起初不断+1,到达右边界之后,不变
            dC = dR == endR ? dC + 1 : dC;//rx坐标起初微微0,到达下边界后,不断+1
            dR = dR == endR ? dR : dR + 1;//ry坐标起初不断+1,到达下边界后,不变
            fromUp = !fromUp;//一条对角线打印完之后,直接改变方向
        }
        System.out.println();
    }

    /**
     * 打印t点和r点之间的点的方法
     * @param m
     * @param tR
     * @param tC
     * @param dR
     * @param dC
     * @param f
     */
    public static void printLevel(int[][] m, int tR, int tC, int dR, int dC,
                                  boolean f) {
        if (f) {
            while (tR != dR + 1) {
                System.out.print(m[tR++][tC--] + " ");
            }
        } else {
            while (dR != tR - 1) {
                System.out.print(m[dR--][dC++] + " ");
            }
        }
    }

   public static void main(String[] args) {
        int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
        printMatrixZigZag(matrix);

    }

}
八、在行列都排好序的矩阵中找数

思路:选取右上角或左下角的点为起始点,右上角的点存在这样的特点:当前列下方的点都比它大,当前行左边的数都比他小,

利用这个特点,每次比较就可以排除一行或一列的点,其时间复杂度最大为O(m+n);

package FaceQuestion;

/**
 * 在已排好序的矩阵中判断一个数是否存在
 */
public class FindNumInSortMatrix {

    public boolean isContains(int[][] matrix,int num){
        int row = 0;
        int col = matrix[0].length - 1;
        while (row < matrix.length && col > -1) {
            if (matrix[row][col] == num) {
                return true;
            } else if (matrix[row][col] > num) {
                col--;
            } else {
                row++;
            }
        }
        return false;
    }
}

猜你喜欢

转载自blog.csdn.net/pgg_cold/article/details/80800742