leetcode--剑指offer--面试题29. 顺时针打印矩阵

一.题目描述

leetcode--剑指offer--面试题29. 顺时针打印矩阵_第1张图片

二.解法与思路

leetcode--剑指offer--面试题29. 顺时针打印矩阵_第2张图片

法一:模拟路径法(switch)(原始方法)

思路:
1.设置数组dir存放 上下左右四个边界。
2.用switch语句展示了四种不同的情况。(上->下,右->左,下->上,左->右)
2.模拟计算机进行分析,遇到边界,通过k值的改变,来选取适应的情况。
4.用存放的数字数目作为终止条件。

代码如下:

public class Test1 {
		public static void main(String[] args){
			int a[][] = {{1,2,3},{4,5,6},{7,8,9}};
			int t[] = new int[a[0].length * a.length];
			int dir[] = {a[0].length,a.length,-1,0};
			int k = 0;
			int i = 0,j = 0;
			int count = 0;
			while(count != a[0].length * a.length) {
					switch(k) {
					case 0: {
						while(j != dir[k])
							t[count++] = a[i][j++];
						//下面这步很难看吧,但是无法再更好的优化了,对比第二种解法
						i++;
						dir[k] = --j;
						k = (k+1)%4;
						break;							
					}				
					case 1:{
						while(i != dir[k]) 
							t[count++] = a[i++][j];
						j--;
						dir[k] = --i;
						k = (k+1)%4;
						break;
					}
					case 2:{
						while(j != dir[k])
							t[count++] = a[i][j--];							
						i--;
						dir[k] = j++;
						k = (k+1)%4;
						break;
					}
					case 3:{
						while(i != dir[k])
							t[count++] = a[i--][j];
						j++;
						dir[k] = i++;
						k = (k+1)%4;
						break;						
					}			
				}
		}
		for(int s : t) {
			System.out.println(s);
		}
	}
}


法二:模拟路径法(边界判断并更新)

思路:
1.设置四个方向存放上下左右四个边界

2.模拟计算机进行分析,比第一种方法更新了不少,其实这个走法就是(右下左上)的顺序,不会改变,switch语句显得麻烦。

3.通过左边界一定大于右边界,上边界一定大于下边界做边界判断;同时做了边界更新,使得代码更加漂亮。(说一下为啥要更新:eg:第一行跑完要跑最后一列,如果top值不更新,在下一过程中,第一行最后一个值会再打印一遍,这是我们不希望看到的。)

4.死循环里写代码实在是让人很难受,还用了break大大提高了运行时间,可是博主实力欠缺,实在不知道怎么优化了,如果大佬们有想法,帮帮孩子吧。。。

代码如下:

public class Test2 {
	public static void main(String[] args){
		int a[][] = {{1,2,3},{4,5,6},{7,8,9}};
		int t[] = new int[a[0].length * a.length];
		int top = 0 , bottom = a.length-1 , left = 0 , right = a[0].length-1;
		int count = 0;
		while(true) {
			for(int j = left; j <= right; j++)
				t[count++] = a[top][j];
			//非常的巧妙,通过这中方法,既完成了边界判断,又将边界值更新,避免了重复赋值	
			//右到左完了,该上到下了,下一步要用到top,top值必须更新,所以++top与bottom相比,后面的同理
			if(++top > bottom)
				break;
			for(int i = top; i <= bottom; i++)
				t[count++] = a[i][right];
			if(--right < left)
				break;
			for(int j = right; j >= left; j--)
				t[count++] = a[bottom][j];
			if(--bottom < top)
				break;
			for(int i = bottom; i >= top; i--)
				t[count++] = a[i][left];
			if(++left > right)
				break;
		}	
		
	for(int s : t) {
		System.out.println(s);
	}
}
}

法三:模拟路径法(方向数组)

思路:
1.设置方向二维数组存放要需要下一步的操作;设置二维数组judge来给每一个值做标记,记录他们是否被访问过。

2.模拟计算机进行分析,设置暂存值row1,row2来暂存执行完后更新的行列。若row1,row2没有超过数组的边界并且该位置的值第一访问,那么盘他。如果超出边界,那么拐弯。(k值加一,在方向数组中执行下一步操作)(由于拐弯了说明还没有访问完数组的元素,那么拐弯后的值必定没有越界,不用多余判断)

3.通过操作步数不大于数组中存放数据的个数来判断是否访问完全部数据。与法一方法思想相同,但是换了个角度,思路更加清晰。

代码如下:

public class Test3 {
	public static void main(String[] args){
		int a[][] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
		int t[] = new int[a[0].length * a.length];
		int top = 0 , bottom = a.length-1 , left = 0 , right = a[0].length-1;
		int k = 0;
		int row = 0,col = 0;
		int row1 = 0,col1 = 0;
		//{0,1}代表列值加一,行值不变,其他同理
		int direction[][] = {{0,1},{1,0},{0,-1},{-1,0}};
		boolean judge[][] = new boolean[a.length][a[0].length];

		for(int step = 0; step < a.length * a[0].length; step++) {
			row1 = row + direction[k][0];
			col1 = col + direction[k][1];
			if(row1 >= top && row1 <= bottom && col1 >= left && col1 <= right && judge[row1][col1] == false && step != 0) {
				t[step] = a[row1][col1];
				judge[row1][col1] = true;
				row = row1 ;
				col = col1 ;
			}
			else {
				if(step != 0) {
					k = (k + 1) % 4;
					row = row + direction[k][0];
					col = col + direction[k][1];	
				}
				t[step] = a[row][col];
				judge[row][col] = true;
			}				
		}

	for(int s : t) {
		System.out.println(s);
	}
}
}

法四:按层遍历法(找规律)

leetcode--剑指offer--面试题29. 顺时针打印矩阵_第3张图片
思路:
1.找规律发现,上下左右四个临界点:A(i,j) B(i,n-j-1) C(m-i-1,n-j-1) D(m-i-1,j)

2.先从A点向C点出发(满足A点横坐标,纵坐标不大于C点更左边即可)

3.考虑下面两种特殊情况(也就是一行一列):①{[2,3,4,5,6]} ② {[1];[2];[3]}

①C点其实是最后一点,经过2步骤的操作已经结束了左右数据的访问,那么久不再考虑下面的操作了。
②C点其实是最后一个数组的唯一点,经过2步骤的操作也不需要在进行下面的操作。

4.if(i < m-i-1 && j < n-j-1) == true说明它不是线性的,而是面,那么就考虑C到D点与D到A点的路径访问操作。

代码如下:

// 上下左右四个临界点:(i,j) (i,n-j-1) (m-i-1,n-j-1)(m-i-1,j)
public class Test4 {
	public static void main(String[] args){
		int a[][] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
		int t[] = new int[a[0].length * a.length];
		int i = 0,j = 0;
		int k = 0;
		int m = a.length;
		int n = a[0].length;
		while(i <= m-i-1 && j <= n-j-1 ) {
			for(int col = j; col <= n-j-1; col++) 
				t[k++] = a[i][col];
			for(int row = i+1; row <= m-i-1; row++)
				t[k++] = a[row][n-j-1];
			if(i < m-i-1 && j < n-j-1) {
				for(int col = n-j-2; col > j; col--)
					t[k++] = a[m-i-1][col];
				for(int row = m-i-1; row >i; row--)
					t[k++] = a[row][j];
			}
			i++;
			j++;
		}

			for(int s : t) {
				System.out.println(s);
			}
	}
}

参考:
https://xxoo521.com/algorithm/
https://leetcode-cn.com/problems/shun-shi-zhen-da-yin-ju-zhen-lcof/solution/mian-shi-ti-29-shun-shi-zhen-da-yin-ju-zhen-she-di/

你可能感兴趣的:(算法)