一、用数组结构实现大小固定的队列和栈
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; /** * 定长数组实现栈 * 设置标志位,start和end * 从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]; } /** * 获取start和end的移动的下一个位置,如果到了边界,那么就置为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; } //交换一下help和queue的引用 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。 * * * 思路: * 使用两个hashmap,HashMap<T,Integer> keyIndexMap和HashMap<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中存储的所有记录个数,同时保证HashMap中Integer为连续的正整数,我们就可以通过这个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;//t点x坐标起初不断+1,到达右边界之后,不变 dC = dR == endR ? dC + 1 : dC;//r点x坐标起初微微0,到达下边界后,不断+1 dR = dR == endR ? dR : dR + 1;//r点y坐标起初不断+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; } }