版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
1. 设计一个有getMin功能的栈
题目:实现一个特殊的栈,在实现栈的基本功能上,再实现返回栈中最小的元素的操作
要求:
- pop、push、getMin操作时间复杂度都是O(1)
- 设计的栈类型可以使用现成的栈结构
代码:
public class Chapter1_1 {
private Stack<Integer> stackData;//数据栈,压栈的数据
private Stack<Integer> stackMin;//辅助栈,从栈顶到栈底 由小到大有序的数据
public Chapter1_1(){
stackData = new Stack<Integer>(); //在new的时候 初始化stackData内存空间
stackMin = new Stack<Integer>(); //在new的时候 初始化stackMin内存空间
}
//压入栈中
public void push(int value){
//将数据压入栈中
stackData.push(value);
//如果辅助栈是空的则直接压入
if(stackMin.isEmpty()){
stackMin.push(value);
}else if(value<stackMin.peek()){
//value比栈顶元素小
stackMin.push(value);
}
}
//弹栈
public int pop(){
//如果数据栈中已空,则 返回 -1
if(stackData.isEmpty()){
return -1;
}
//数据栈弹出
int value = stackData.pop();
if(value ==stackMin.peek()){
stackMin.pop();
}
return value;
}
//获取最小值
public int getMin(){
//获取辅助栈顶元素即可
return stackMin.peek();
}
}
2. 由两个栈组成的队列
题目:
用两个栈实现队列,可以进行 add、poll、peek 操作
代码:
public class Chapter1_2 {
private Stack<Integer> stack01;//数据栈,压栈的数据
private Stack<Integer> stack02;//辅助栈,stack01的逆序
public Chapter1_2(){
stack01 = new Stack<Integer>(); //在new的时候 初始化stack01内存空间
stack02 = new Stack<Integer>(); //在new的时候 初始化stack02内存空间
}
//队尾入
public void add(int value){
//将辅助栈中的元素转移到 01栈中,保证新的元素在以前元素的后面
while(!stack02.isEmpty()){
stack01.push(stack02.pop());
}
stack01.push(value);
}
//队头出
public int poll(){
//如果01栈和02栈都为空,则说明 队列为空了
if(stack01.isEmpty() && stack02.isEmpty() ){
return -1;
}
//如果02不为空 则说明 是连续的出队列操作,此时所有的元素都在02栈中,直接出02栈即可
if(!stack02.isEmpty() ){
return stack02.pop();
}
//02栈为空,将01栈中的元素转移到 02栈中,保证01最底部的元素在02的栈顶
while(!stack01.isEmpty()){
stack02.push(stack01.pop());
}
return stack02.pop();
}
//查看队头数据
public int peek(){
//如果02栈为空,则说明 队列为空了
if(stack02.isEmpty() ){
return -1;
}
return stack02.pop();
}
public boolean isEmpty(){
//如果01栈和02栈都为空,则说明 队列为空了
return stack01.isEmpty() && stack02.isEmpty();
}
}
- 猫狗队列
题目:
通过给定的类实现猫狗队列
代码:
public class Chapter1_4 {
private static final String DOG ="dog";
private static final String CAT ="cat";
private Queue<PetQueue> dogQueue ; //存放狗的队列
private Queue<PetQueue> catQueue; //存放猫的队列
private long count;//标记存放先后顺序的
public Chapter1_4(){
this.dogQueue = new LinkedList<PetQueue>();//初始化
this.catQueue = new LinkedList<PetQueue>();//初始化
this.count = 0;//初始化
}
public void add(Pet pet){
if(DOG.equalsIgnoreCase(pet.getType())){
dogQueue.offer(new PetQueue(pet,this.count++));
}else{
catQueue.offer(new PetQueue(pet,this.count++));
}
}
//取出队列中最早进入队列的宠物
public Pet pollAll(){
//如果 两个队列中都有值
if(!dogQueue.isEmpty() && !catQueue.isEmpty()){
//如果dog的存放的编号小于cat的编号,则 说明dog比cat存放的早
if(dogQueue.peek().getNumber()<catQueue.peek().getNumber()){
return dogQueue.poll().getPet();
}else{
return catQueue.poll().getPet();
}
}else if(!dogQueue.isEmpty()) {
//如果dog队列中有值而 cat队列已为空
return dogQueue.poll().getPet();
}else if(!catQueue.isEmpty()){
//如果cat队列中有值而 dog队列已为空
return catQueue.poll().getPet();
}else{
//都为空
return null;
}
}
//判断对列中是否都为空
public boolean isEmpty(){
return dogQueue.isEmpty() && catQueue.isEmpty();
}
//判断dog队列中是否都为空
public boolean dogIsEmpty(){
return dogQueue.isEmpty();
}
//判断cat队列中是否都为空
public boolean catIsEmpty(){
return catQueue.isEmpty();
}
}
4. 用一个栈实现另一个栈的排序
题目:
将一个存放整数的栈,从栈顶到栈底 由小到大排列,只能用一个辅助栈,可以用辅助变量,不能用其他数据结构
代码:
public class Chapter1_5 {
//借助一个辅助栈排序
public Stack<Integer> sort(Stack<Integer> stack){
Stack<Integer> heleStack = new Stack<Integer>();//辅助栈
int vlaue = 0; //辅助变量 暂存栈中弹出的元素
while(!stack.isEmpty()){
vlaue = stack.pop();
while(!heleStack.isEmpty() && vlaue< heleStack.peek()){
stack.push(heleStack.pop());
}
heleStack.push(vlaue);
}
// 此时 辅助栈中栈顶到栈底 是从大到小的,再放进原栈中,则元素为从小到大
while(!heleStack.isEmpty()){
stack.push(heleStack.pop());
}
return stack;
}
}
5. 用栈实现汉诺塔问题
题目:
用栈实现汉诺塔,不能直接从左移到右,或者从右移到左,必须经过中间柱子。
代码:
public class Chapter1_6 {
public static final String LEFT = "left";
public static final String MID = "mid";
public static final String RIGHT = "right";
public int recMoveHanoi(int n, String left, String mid, String right) {
if (n < 1) {
return 0;
}
return recMoveHanoi(n, left, mid, right, LEFT, RIGHT);
}
//递归实现
public int recMoveHanoi(int n, String left, String mid, String right, String from, String to) {
//递归的终止条件 n==1
if (n == 1) {
//从左或右 移动到中间的,或者从中间移动到左或者右的,都只要一步
if (MID.equalsIgnoreCase(from) || MID.equalsIgnoreCase(to)) {
System.out.println("move 1 from " + from + " to " + to);
return 1;
} else {
System.out.println("move 1 from " + from + " to mid");
System.out.println("move 1 from mid to " + to);
return 2;
}
}
//从左或右 移动到中间的,或者从中间移动到左或者右的,都只要一步
if (MID.equalsIgnoreCase(from) || MID.equalsIgnoreCase(to)) {
//获取对应的作为辅助的柱子,例如 from为mid ,to为left,则 right为辅助的柱子,
String another = LEFT.equalsIgnoreCase(to) || LEFT.equalsIgnoreCase(from) ? RIGHT : LEFT;
int step1 = recMoveHanoi(n - 1, left, mid, right, from, another);
int step2 = 1;
System.out.println("move " + n + " from " + from + " to " + to);
int step3 = recMoveHanoi(n - 1, left, mid, right, another, to);
return step1 + step2 + step3;
} else {
//从左到右 或者 从右到左
//1.此时 n在form上,要经过mid,才能到to ,所以先将to作为辅助,将 n-1 移到to上
int step1 = recMoveHanoi(n - 1, left, mid, right, from, to);
//2.此时 n在form上, n-1在to上,所以将n移到mid上
int step2 = 1;
System.out.println("move " + n + " from " + from + " to mid");
//3.此时 n-1在to上,n在mid上,from作为辅助 ,所以先将n-1移到from上
int step3 = recMoveHanoi(n - 1, left, mid, right, to, from);
//4.此时 n-1在from上,n在mid上,将n移到to上
int step4 = 1;
System.out.println("move " + n + " from mid to " + to);
//5.此时 n-1在from上,n在to上,所以先将n-1移到to上
int step5 = recMoveHanoi(n - 1, left, mid, right, from, to);
return step1 + step2 + step3 + step4 + step5;
}
}
}
6. 生成窗口最大值数组
题目:
有一个整形数组arr,一个大小为 w 的窗口从数组最左边滑到最右边,每次移动一个位置,输出在每个窗口下的窗口最大值
代码:
public class Chapter1_7 {
public int[] getMaxWindow(int[] arr, int w) {
if (arr == null || arr.length < 1) {
return new int[]{0};
}
//存放最终结果
int length = arr.length;
//比如 length =4,w=3, 则有两个窗口最大值
int[] res = new int[length - w + 1];
int index = 0;
//存放数组的索引
LinkedList<Integer> qmax = new LinkedList<Integer>();
//循环数组
for (int i = 0; i < arr.length; i++) {
while (!qmax.isEmpty() && arr[qmax.peekLast()] < arr[i]) {
qmax.pollLast();
}
qmax.offerLast(i);
//如果 qmax中第一个存放的索引失效,则删除,比如 i=5,w=3,则 5-3=2 索引2和2之前的都是失效的
if (qmax.peekFirst() <= i - w) {
qmax.pollFirst();
}
//最大值结果是从 i>=w-1开始的记录的,例如 i=1 ,w=3 ,3-1=2, 此时还未到第一个窗口
if (i >= w - 1) {
res[index++] = arr[qmax.peekFirst()];
}
}
return res;
}
}
7. 构造数组的maxTree
题目:
public class Chapter1_8 {
public Node getMaxTree(int[] arr) {
if (arr == null) {
return null;
}
int length = arr.length;
Node[] nodes = new Node[length];
//初始化Node数组
for (int i = 0; i < length; i++) {
nodes[i] = new Node(arr[i]);
}
//存放当前节点往左 第一个大于当前节点的节点
Map<Node, Node> leftMap = new HashMap<Node, Node>();
//存放当前节点往右 第一个大于当前节点的节点
Map<Node, Node> rightMap = new HashMap<Node, Node>();
//存放遍历到节点
Stack<Node> stack = new Stack<Node>();
for (int i = 0; i < length; i++) {
//关键地方,判断栈顶元素的值是否小于当前元素的值,如果小于则在map中记录栈顶元素的往左数第一个元素
while (!stack.isEmpty() && stack.peek().value < nodes[i].value) {
popStackToMap(stack, leftMap);
}
//存放当前元素,前提保证栈中没有比小的元素
stack.push(nodes[i]);
}
//如果栈中还有元素,则循环获取栈中元素的往左数第一个大于他的元素
while (!stack.isEmpty()) {
popStackToMap(stack, leftMap);
}
for (int i = length - 1; i >= 0; i--) {
//关键地方,判断栈顶元素的值是否小于当前元素的值,如果小于则在map中记录栈顶元素的往右数第一个元素
while (!stack.isEmpty() && stack.peek().value < nodes[i].value) {
popStackToMap(stack, rightMap);
}
//存放当前元素,前提保证栈中没有比小的元素
stack.push(nodes[i]);
}
//如果栈中还有元素,则循环获取栈中元素的往右数第一个大于他的元素
while (!stack.isEmpty()) {
popStackToMap(stack, rightMap);
}
//再将 每个元素 左右 最大的元找见后,开始构造MaxTree
Node head = null;
for (int i = 0; i < length; i++) {
Node node = nodes[i];
Node leftNode = leftMap.get(node);
Node rightNode = rightMap.get(node);
//左右最大的元素都为空,则此元素是数组中最大,作为根节点
if (leftNode == null && rightNode == null) {
head = node;
} else if (leftNode == null) {
if (rightNode.left == null) {
rightNode.left = node;
} else {
rightNode.right = node;
}
} else if (rightNode == null) {
if (leftNode.left == null) {
leftNode.left = node;
} else {
leftNode.right = node;
}
} else {
Node parent = leftNode.value < rightNode.value ? leftNode : rightNode;
if (parent.left == null) {
parent.left = node;
} else {
parent.right = node;
}
}
}
return head;
}
public void popStackToMap(Stack<Node> stack, Map<Node, Node> map) {
Node node = stack.pop();
if (stack.isEmpty()) {
map.put(node, null);
} else {
map.put(node, stack.peek());
}
}
//简单的用递归中序遍历一下生成的MaxTree
public void recInOrder(Node head) {
if (head == null) {
return;
}
recInOrder(head.left);
System.out.print(head.value + " ");
recInOrder(head.right);
}
}
8. 求最大矩阵大小
题目:
给定一个整形数组 map,其中值只有0和1两种,求所有全是1的所有子矩阵中,最大的矩阵全是1的数量
代码:
public class Chapter1_9 {
public int maxSubMatrixSize(int[][] map) {
if (map == null) {
return 0;
}
int[] height = new int[map[0].length];
int maxSize = 0;
for (int i = 0; i < map.length; i++) {
//每一行中,子矩阵的高度
for (int j = 0; j < map[0].length; j++) {
height[j] = map[i][j] == 0 ? 0 : height[j] + 1;
}
maxSize = Math.max(getMatrixMaxSize(height), maxSize);
}
return maxSize;
}
public int getMatrixMaxSize(int[] height) {
if (height == null || height.length == 0) {
return 0;
}
int maxSize = 0;
Stack<Integer> stack = new Stack<Integer>();
for (int i = 0; i < height.length; i++) {
while (!stack.isEmpty() && height[i] <= height[stack.peek()]) {
int j = stack.pop();
int k = stack.isEmpty() ? -1 : stack.peek();
//例如: i=6,k =4 则 元素的边界为4到6,
maxSize = Math.max((i - k - 1) * height[j], maxSize);
}
stack.push(i);
}
//栈中可能还有未计算完的元素,此时height的长度作为元素的边界
while (!stack.isEmpty()) {
int j = stack.pop();
int k = stack.isEmpty() ? -1 : stack.peek();
maxSize = Math.max((height.length - k - 1) * height[j], maxSize);
}
return maxSize;
}
}
9. 最大值减去最小值等于num的子数组数量
题目:
给定整数数组arr和整数num,共返回多少的数组满足如下情况
max(arr[i…j]) - min(arr[i…j]) <= num
max(arr[i…j])表示数组arr[i…j] 中最大值,min(arr[i…j])表示数组arr[i…j] 中最小值
代码:
public class Chapter1_10 {
public int getNum(int[] arr, int k) {
//存放最大值的索引
LinkedList<Integer> qmax = new LinkedList<Integer>();
//存放最小值的索引
LinkedList<Integer> qmin = new LinkedList<Integer>();
int i=0;
int j=0;
int res = 0;
while (i < arr.length) {
while (j < arr.length) {
while (!qmax.isEmpty() && arr[qmax.peekLast()] < arr[j]) {
qmax.pollLast();
}
qmax.offerLast(j);
while (!qmin.isEmpty() && arr[qmin.peekLast()] > arr[j]) {
qmin.pollLast();
}
qmin.offerLast(j);
//左边界为i 右边界向右能扩展的最大距离
if (arr[qmax.peekFirst()] - arr[qmin.peekFirst()] > k) {
break;
}
j++;
}
if (i == qmax.peekFirst()) {
qmax.pollFirst();
}
if (i == qmin.peekFirst()) {
qmin.pollFirst();
}
res += j - i;
i++;
}
return res;
}
}
10. 有效的括号
题目:
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
代码:
class Solution {
public boolean isValid(String s) {
if("".equals(s))
{
return true;
}
if(s.length() %2 != 0)
{
return false;
}
Map<Character, Integer> map = new HashMap<>();
map.put('(',1);
map.put(')',-1);
map.put('[',2);
map.put(']',-2);
map.put('{',3);
map.put('}',-3);
char [] arr = s.toCharArray();
Stack<Integer> stack = new Stack<>();
stack.push(map.get(arr[0]));
for(int i=1;i<arr.length;i++)
{
if(stack.isEmpty())
{
stack.push(map.get(arr[i]));
}
else if(map.get(arr[i]) + stack.peek() == 0)
{
stack.pop();
}
else{
stack.push(map.get(arr[i]));
}
}
if(stack.isEmpty())
{
return true;
}
return false;
}
}