算法实现----栈和队列(二)

实现栈排序

要求: 一个栈中元素为整型,实现一个方法将该栈从顶到底按照从大到小的顺序排列
只能申请一个栈和有限的变量

import java.util.Stack;

public class SortStackByStack {
	public static void main(String[] args) {
		Stack<Integer> stack = new Stack<Integer>();
		stack.push(4);
		stack.push(1);
		stack.push(8);
		stack.push(7);
		stack.push(5);
		stack.push(9);
		stack.push(2);
		stack.push(6);
		sort(stack);
		while(!stack.empty()) {
			System.out.println(stack.pop());
		}
	}
	
	public static void sort(Stack<Integer> stack) {
		if(stack.empty()) {
			return;
		}
		Stack<Integer> tempStack = new Stack<Integer>();
		while(!stack.empty()) {
			int temp = stack.pop();
			while(!tempStack.empty() && tempStack.peek() < temp) {
				stack.push(tempStack.pop());
			}
			tempStack.push(temp);
		}
		while(!tempStack.empty()) {
			stack.push(tempStack.pop());
		}
		
	}
}

生成窗口最大值数组

要求:
给出一个整形数组,例如arr = {5,4,3,5,6,7,6},窗口大小为w=3,窗口每次向右移动一位,输出每个窗口中最大值组成的数组。
例如:
* [5,4,3,]5,6,7,6     窗口最大值为5
* 5,[4,3,5,]6,7,6     窗口最大值为5
* 5,4,[3,5,6,]7,6     窗口最大值为6
* 5,4,3,[5,6,7,]6     窗口最大值为7
* 5,4,3,5,[6,7,6]     窗口最大值为7
*
* 则输出的数组为{5,5,6,7,7};
*
* 思路:本题如果依次遍历然后比较,只能做出时间复杂度为O(W*N)的解法
* 可以利用双端队列,做出时间复杂度为O(N)的解法,关键在于当遍历的数大于队列里的数时,
* 可以将队列里的数依次弹出(此时里面的小数已经无用),做出一个从大到小排列的队列,
* 再判断队头最大值是否在窗口中即可。
* 所有的遍历数据最多进队列一次,出队列一次,比较次数最多也只是N的常数倍,可以做到时间复杂度为O(n)

public class WindowMaxArr {

	public static void main(String[] args) {
		int[] arr = {5,4,3,5,6,7,6};
		int[] result = getWindowMaxArr(arr, 3);
		System.out.println(Arrays.toString(result));
	}
	
	public static int[] getWindowMaxArr(int[] arr,int w) {
		if(arr == null || arr.length < w || w < 1) {
			return null;
		}
		int[] result = new int[arr.length + 1 - w];
		int index = 0;
		LinkedList<Integer> queue = new LinkedList<Integer>();
		
		for(int i = 0;i < arr.length;i++) {
			/*  自写劣质版
			if(queue.isEmpty()) {
				
			}else if(arr[queue.getLast()] > arr[i]){
				
			}else if(arr[queue.getLast()] <= arr[i]) {
				while(!queue.isEmpty() && arr[queue.getLast()] <= arr[i] ) {
					queue.pollLast();
				}
			}
			queue.add(i);
			if(i >= w - 1) {
				while(queue.getFirst() < i + 1 - w ) {
					queue.pollFirst();
				}
				result[index++] = arr[queue.getFirst()];
			}
			*/
			//简洁正宗版
			while(!queue.isEmpty() && arr[queue.getLast()] <= arr[i]) {
				queue.pollLast();
			}
			
			queue.add(i);
			
			if(queue.getFirst() == i - w) {
				queue.pollFirst();
			}
			if(i >= w - 1) {
				result[index++] = arr[queue.getFirst()];
			}
			
		}
		return result;
	}
	

}

单调栈结构

要求:给定一个不含有重复值的数组arr, 找到每一个i位置左边和右边离i位置最近且值比arr[i]小的值索引 返回所有位置信息。
* 例如:arr={3,4,1,5,6,2,7}
* 返回值为:
* {{-1,2},{0,2},{-1,-1},{2,5},{3,5},{2,-1},{5,-1}}
* 进阶问题:数组arr可能存在重复值
*

public class NearLess {

	public static void main(String[] args) {
		int[] arr = { 3,4,1,5,6,2,7};
		int[][] result = rightWay01(arr);
		int[][] result2 = rightWay02(arr);
		int[][] result3 = rightWay03(arr);
		
		for (int[] is : result) {
			System.out.print(Arrays.toString(is) + "  ");
		}
		System.out.println();
		for (int[] is : result2) {
			System.out.print(Arrays.toString(is) + "  ");
		}
		System.out.println();
		for (int[] is : result3) {
			System.out.print(Arrays.toString(is) + "  ");
		}
		
	}

	// 1.时间复杂度为O(n²)
	public static int[][] rightWay01(int[] arr){
		int[][] result = new int[arr.length][2];
		for(int i = 0;i < arr.length;i++) {
			int j = i - 1;
			int k = i + 1;
			//向前遍历
			for(;j >= 0;j--) {
				if(arr[j] < arr[i]) {
					break;
				}
			}
			result[i][0] = j;
			
			//向后遍历
			for(;k < arr.length;k++) {
				if(arr[k] < arr[i]) {
					break;
				}
			}
			if(k == arr.length) {
				result[i][1] = -1;
			}else {
				result[i][1] = k;
			}
		}
		
		return result;
	}
	
	//2.不含重复值,时间复杂度为O(n)
	//思路:遍历数组内元素,利用栈存储,并将栈设计成单调形式
    //若求元素左右小于该元素的值索引,则从栈底到栈顶为递增;
    //若求元素左右大于该元素的值索引,则从栈底到栈顶为递减。
    //在此题中,遍历时若该元素入栈时不构成单调递增,
    //则弹出栈中数据,直至元素压入栈后为单调;
    //压入栈的元素左边最近小值则为此时的栈顶,
    //弹出栈的元素右边最近小值则为此时遍历的元素索引。
	public static int[][] rightWay02(int[] arr){
		if(arr == null || arr.length == 0) {
			return null;
		}
		int[][] result = new int[arr.length][2];
		Stack<Integer> stack = new Stack<>();
		for(int i = 0;i < arr.length;i++) {
			while(!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
				result[stack.pop()][1] = i;
			}
			if(stack.isEmpty()) {
				result[i][0] = -1;
			}else {
				result[i][0] = stack.peek();
			}
			stack.push(i);
		}
		while(!stack.isEmpty()) {
			result[stack.pop()][1] = -1;
		}
		return result;
	}
	
	//3.进阶问题:数组arr可能存在重复值,时间复杂度为O(n)
	public static int[][] rightWay03(int[] arr){
		if(arr == null || arr.length == 0) {
			return null;
		}
		int[][] result = new int[arr.length][2];
		Stack<Integer> stack = new Stack<>();
		for(int i = 0;i < arr.length;i++) {
			while(!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
				result[stack.pop()][1] = i;
			}
			//有重复值时,需多加一个判断
			if(stack.isEmpty()) {
				result[i][0] = -1;
			}else if(arr[i] == arr[stack.peek()]){
				result[i][0] = result[stack.peek()][0];
			}else {
				result[i][0] = stack.peek();
			}
			stack.push(i);
		}
		while(!stack.isEmpty()) {
			result[stack.pop()][1] = -1;
		}
		return result;
	}
}

求最大子矩阵的大小

    给定一个整型矩阵map,其中的值只有0和1两种,求其中全是1的所有矩形区域中,最大的矩形区域为1的数量。
例如:
1,1,1,0
其中最大的矩形区域有3个1,所以返回3。
再如:
1,0,1,1
1,1,1,1
1,1,1,0
其中,最大的矩形区域有6个1,所以返回6。

public class MaxRecSize {

	public static void main(String[] args) {
		int[] arr = {3,4,5,4,3,6};
		int maxAreaByHeight = getMaxAreaByHeight(arr);
		System.out.println(maxAreaByHeight);
		
		int[][] arr2 = {{1,0,1,1},{1,1,1,1},{1,1,1,0}};
		int maxRecSize = maxRecSize(arr2);
		System.out.println(maxRecSize);
	}
	/*
	 * 给定一个整型矩阵map,其中的值只有0和1两种,求其中全是1的所有矩形区域中,最大的矩形区域为1的数量。
	 *	例如:
	 *	1,1,1,0    
	 *	其中最大的矩形区域有3个1,所以返回3。
	 *	再如:
	 *	1,0,1,1
	 *	1,1,1,1
	 *	1,1,1,0   
	 *	其中,最大的矩形区域有6个1,所以返回6。
	 *
	 *	思路:矩阵的行数为N,以每一行切割,然后统计以当前行作为底的情况下,
	 *	      每个位置往上的连续1的数量。
	 *	      那么可以将1的数量转化成最大矩形的面积,每一行切割,
	 *	      如何依次遍历比较更新最大值。
	 *	      考察每一个矩阵向左右扩展后形成的最大矩阵,可以使用单调栈,
	 *	      转化成求当前i位置左右最近小值的索引,此时的索引相减即为矩形的底,
	 *	      i索引的值即为高。
	 */
	public static int maxRecSize(int[][] arr) {
		int[] height = new int[arr[0].length];
		int max = 0;
		for(int i = 0;i < arr.length;i++) {
			for(int j = 0;j < arr[0].length;j++) {
				if(i == 0) {
					height[j] = arr[i][j];
				}else {
					height[j] = arr[i][j] == 0 ? 0 : height[j] + 1;
				}
			}
			max = Math.max(max, getMaxAreaByHeight(height));
		}
		return max;
	}
	
	public static int getMaxAreaByHeight(int[] height) {
		if(height == null || height.length == 0) {
			return 0;
		}
		int[][] range = new int[height.length][2];
		Stack<Integer> stack = new Stack<Integer>();
		int max = 0;
		for(int i = 0;i < height.length;i++) {
			while(!stack.isEmpty() && height[i] < height[stack.peek()]) {
				range[stack.pop()][1] = i;
			}
			if(!stack.isEmpty() && height[stack.peek()] == height[i]) {
				range[i][0] = range[stack.peek()][0];
			}else if(stack.isEmpty()){
				range[i][0] = -1;
			}else {
				range[i][0] = stack.peek();
			}
			stack.push(i);
		}
		
		while(!stack.isEmpty()) {
			range[stack.pop()][1] = height.length;
		}
		
		for(int i = 0;i < height.length;i++) {
			max = Math.max(max, height[i]*(range[i][1]-range[i][0] - 1));
		}
		return max;
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值