南昌大学计算机考研机试练习题

南昌大学计专机试练习题(二)

  • 11.数组中重复的数字
  • 12.三数之和
  • 13.验证回文字符串
  • 14.Z字形变换
  • 15.设计一个getMin的栈
  • 16.用栈解决汉诺塔问题
  • 17.判断二叉树是否为平衡二叉树
  • 18.二叉树的中序遍历
  • 19.只出现一次的数字
  • 20.扩展“只出现一次的数字”
  • 21.扩展“只出现一次的数字”

11.数组中重复的数字

题目描述:在一个长度为n的数组里的所有数字都在0到n-1的范围内,不考虑溢出情况。数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。如[2,3,1,0,2,5,3],输出2
思路:方法1,由于数字在0-n-1范围内,可以将数组元素映射到以该数组元素为下标的数组中(通过加上数组长度n来实现),如果有某个元素值大于或等于数组长度n,说明该值已被访问。
方法2,排序之后再查找相邻元素是否相等。
方法3,利用一个辅助数组,遍历原数组时,将数组元素作为辅助数组下标,标记辅助数组。

public static int getFirstRepeatElement(int a[]) {
       // +n解法  将第i个元素是否访问的标志映射到第a[i]个元素上
	for (int i = 0 ; i < a.length ; i++) {
     
		int index = a[i] >= a.length ? a[i] - a.length : a[i];  // 取a[i]原来的值
		if (a[index] >= a.length) return index;  // 访问过
		a[index] += a.length;  // 未访问过加数组长度
	}
	return -1;
}
public static int getFirstRepeatElement2(int a[]) {
       // 排序解法
	Arrays.sort(a);
	for (int i = 0 ; i < a.length - 1 ; i++) {
     
		if (a[i] == a[i+1]) return a[i];
	}
	return -1;
}
public static int getFirstRepeatElement3(int a[]) {
       // 辅助数组标志解法
	int b[] = new int[a.length];
	for (int i = 0 ; i < a.length ; i++) {
     
		if (b[a[i]] != 1) {
     
			b[a[i]] = 1;
		} else return a[i];
	}
	return -1;
}

12.三数之和

题目描述:给定一个包含n个整数的数组nums。判断nums中是否存在三个元素a,b,c使得a+b+c=0?找出所有满足条件且不重复的三元组。如nums=[-1,0,1,2,-1,-4],返回[[-1,0,1],[-1,-1,2],
思路:基本思路是先排序然后选中三元组的一个元素,然后枚举其他两个元素。方法1是选中中间一个数,然后向左右枚举,满足条件则添加到列表里面,最后通过set去重。方法2同样是选中中间一个数,然后向左右枚举,在遍历的时候去重,需考虑连续三个及以上零的情况。方式三,选中第一个数,然后枚举第二和第三个数,遍历时去重。

public static Set<String> getThreeSum(int a[]) {
       // 遍历a[i],把a[i]当做三个数的中间数,然后向左右逐渐枚举。最后放入set去重。
	if (a == null || a.length < 3) return new HashSet();
	Arrays.sort(a);
	List<List<Integer>> lists = new ArrayList<List<Integer>>();
	for (int i = 1 ; i < a.length - 1 ; i++) {
     
		int left = i - 1, right = i + 1;
		while (left >= 0 && right <= a.length - 1) {
     
			int sum = a[left] + a[i] + a[right];
			if (sum == 0) {
     
				List<Integer> list = new ArrayList<Integer>();
				list.add(a[left]);list.add(a[i]);list.add(a[right]);
				lists.add(list);
				left--;
				right++;
			} else if (sum < 0) {
     
				right++;
			}else left--;
		}
	}
	Set<String> st = new HashSet<String>();
	for (List list : lists) {
     
		st.add(list.toString());
	}
	return st;
}
public static List<List<Integer>> getThreeSum2(int a[]) {
       // 遍历a[i],把a[i]当做三个数的中间数,然后向左右逐渐枚举。遍历时去重。
	if (a == null || a.length < 3) return new ArrayList<List<Integer>>();
	Arrays.sort(a);
	List<List<Integer>> lists = new ArrayList<List<Integer>>();
	for (int i = 1 ; i < a.length - 1 ; i++) {
     
		if (a[i] == a[i+1]) continue;  // 去重
		if (i >=2 && a[i] == a[i-1] && a[i] == a[i-2] && a[i] == 0) {
       // 特殊元素0
			List<Integer> list = new ArrayList<Integer>();
			list.add(0);list.add(0);list.add(0);
			lists.add(list);
		}
		int left = i - 1, right = i + 1;
		while (left >= 0 && right <= a.length - 1) {
     
			int sum = a[left] + a[i] + a[right];
			if (sum == 0) {
     
				List<Integer> list = new ArrayList<Integer>();
				list.add(a[left]);list.add(a[i]);list.add(a[right]);
				lists.add(list);
				left--;
				right++;
			} else if (sum < 0) {
     
				right++;
			}else left--;
		}
	}
	return lists;
}
public static List<List<Integer>> getThreeSum3(int a[]) {
       // 遍历a[i],把a[i]当做三个数的第一个元素,然后对第二第三个元素逐渐枚举。遍历时去重。
	if (a == null || a.length < 3) return new ArrayList<List<Integer>>();
	Arrays.sort(a);
	List<List<Integer>> lists = new ArrayList<List<Integer>>();
	int first;
	for (int i = 1 ; i < a.length - 1 ; i++) {
     
		if (i > 0 && a[i] == a[i - 1]) continue;  // 第一元素去重
		if ((first = a[i]) > 0) break;  // 第一个元素大于零,其后的所有组合都会使结果大于0
		int mid = i + 1, right = a.length - 1;
		while (mid < right) {
                             
			if (first + a[mid] + a[right] < 0) mid++;  // 数小,则中间指针右移,使结果变大
			else if (first + a[mid] + a[right] > 0) right--;  // 数大,右边指针左移,使结果变小
			else {
     
				lists.add(Arrays.asList(first, a[mid], a[right]));
		 		mid++;right--;
				while (mid < right && a[mid] == a[mid - 1]) mid++;  // 第二元素去重
				while (mid < right && a[right] == a[right + 1]) right--;  // 第三元素去重
			}
		}
	}
	return lists;
}

13.验证回文字符串

题目描述:给定一个非空字符串s,最多删除一个字符。判断是否能成为回文字符串。如"aba",返回true,“abca”,返回true,可删除c。
思路:双指针验证,然后当i和j所在字符不想等时,分别“删除i”(i+1)和“删除j”(j-1)返回它们的或值。

public static boolean isPalindrome(String s) {
      
	int i = 0, j = s.length() - 1;
	while (i < j) {
     
		if (s.charAt(i) != s.charAt(j)) {
     
			return (isValid(s, i + 1, j) || isValid(s, i, j - 1));
		}
		i++;j--;
	}
	return true;
}
public static boolean isValid(String s, int i, int j) {
     
	while (i < j) {
     
		if (s.charAt(i) != s.charAt(j)) {
     
			return false;
		}
		i++;j--;
	}
	return true;
}

14.Z字形变换

题目描述:讲一个给定字符串根据给定的行数,以从上往下、从左到右进行Z字形排列。比如输入"LEETCODEISHIRING",输出:

L     C     I     R
E  T  O  E  S  I  I  G
E     D     H     N

最后输出需要从左往右逐行读取,产生一个新的字符串,即:“LCIRETOESIIGEDHN”。
思路:类“电梯”遍历。将每层的字符放入对应层的集合中,详见代码及注释。

public static String printZ(String s, int n) {
     
	if (n <= 1 || n > s.length()) return s;
	List<List<String>> lists = new ArrayList<List<String>>(n);  // 初始化
	for (int i = 0 ; i < n ; i++) lists.add(new ArrayList<String>());
	boolean flag = true;  // 上下移动方向
	int j = 0;
	for (int i = 0 ; i < s.length() ; i++) {
       // 将原字符串的字符按电梯轮换顺序分配给二维list
		if (j == 0) flag = true;  // 开始向下
		if (j == n - 1) flag = false;  // 开始向上
		lists.get(j).add(String.valueOf(s.charAt(i)));
		if (flag) j++;
		else j--;
	}
	String ans = "";
	for (int i = 0 ; i < lists.size() ; i++) {
     
		for (j = 0 ; j < lists.get(i).size() ; j++) {
     
			ans += lists.get(i).get(j);
			System.out.print(lists.get(i).get(j));  // 输出元素
			if (i == 0 || i == lists.size() - 1) {
       // 第一行和最后一行中元素间隔空格
				int blank = 2 * (lists.size() - 1) - 1;
				while (blank > 0) {
     
					System.out.print(" ");
					blank--;
				}
			} else {
       // 其他情况下的间隔
				if (j % 2 == 0) {
     
					int blank =  2 * (lists.size() - 1) - 1 - (2 * i);
					while (blank > 0) {
     
						System.out.print(" ");
						blank--;
					}
				} else {
     
					int blank = 2 * i - 1;
					while (blank > 0) {
     
						System.out.print(" ");
						blank--;
					}
				}
			}  // 间隔
			
		}
		System.out.println();
	}
	return ans;
}

15.设计一个getMin的栈

题目描述:设计一个特殊的栈,在实现栈的基本功能的情况下,再实现返回栈中最小元素的操作。
思路:利用一个辅助栈,在元素入主栈时,将其与辅助栈顶元素比较,若小或等,则将元素入辅助栈,否则直接入主栈。出栈的时候,若元素与辅助栈顶元素相等,则将辅助栈顶元素一并弹出,否则直接出主栈栈顶元素。这样,getMin可通过辅助栈栈顶元素直接返回栈内最小元素。

public class MinStack {
     

	private Stack<Integer> stack;
	private Stack<Integer> stackMin;
	public MinStack() {
     
		stack = new Stack<Integer>();
		stackMin = new Stack<Integer>();
	}
	
	public void push(int a) {
     
		if (stackMin.isEmpty() || a < getMin()) stackMin.push(a);  // 空或者有更小的,入小栈。
		else stackMin.push(getMin());  // 保持和正常栈元素个数相同,但元素没必要保持。因为出栈时,最小栈要同步出栈。
		stack.push(a);
	}
	public int pop() {
     
		if (stack.isEmpty()) throw new RuntimeException("Your stack is empty.");
		stackMin.pop();
		return stack.pop();
	}
	public int getMin() {
     
		if (stackMin.isEmpty()) throw new RuntimeException("Your stack is empty.");
		return stackMin.peek();
	}
	public static void main(String[] args) {
     
		MinStack minStack = new MinStack();
		minStack.push(0);
		minStack.push(5);
		minStack.push(-8);
		minStack.pop();
		System.out.println(minStack.getMin());
	}
}

16.用栈解决汉诺塔问题

题目描述:用栈而非常规的递归操作去解决。
思路:。。。

public static enum Action{
     
	No, LToM, MToL, MToR, RToM
}
public static int fStackToStack(Action [] record, Action preNoAct, Action nowAct, Stack<Integer> fStack, Stack<Integer> tStack, String from, String to) {
     
	if (record[0] != preNoAct && fStack.peek() < tStack.peek()) {
     
		tStack.push(fStack.pop());
		System.out.println("Move " + tStack.peek() + " from " + from + " to " + to);
		record[0] = nowAct;
		return 1;
	}
	return 0;
}
public static int hanoi2(int n, String left, String mid, String right) {
      
	Stack<Integer> sl = new Stack<Integer>(); sl.push(Integer.MAX_VALUE);
	Stack<Integer> sm = new Stack<Integer>(); sm.push(Integer.MAX_VALUE);
	Stack<Integer> sr = new Stack<Integer>(); sr.push(Integer.MAX_VALUE);
	for (int i = n ; i > 0 ; i--) sl.push(i);
	Action[] record = {
     Action.No};
	int step = 0;
	while (sr.size() != n + 1) {
       // 穷举所有情况直至第三个栈满
		step += fStackToStack(record, Action.MToL, Action.LToM, sl, sm, left, mid);
		step += fStackToStack(record, Action.LToM, Action.MToL, sm, sl, mid, left);
		step += fStackToStack(record, Action.RToM, Action.MToR, sm, sr, mid, right);
		step += fStackToStack(record, Action.MToR, Action.RToM, sr, sm, right, mid);
	}
	return step;
}

17.判断二叉树是否为平衡二叉树

题目描述:给定一棵二叉树的头结点head,判断这颗二叉树是否为平衡二叉树。
思路:后序递归遍历求树高的过程,若有左子树与右子树高度差超过1的情况,标记结果为false并返回,否则最终结果返回true。

public class BalanceBiTree {
     
	public static int TreeDepth2(BiTree root, boolean a[]) {
     
		if (root == null) return 0;
		int left = TreeDepth2(root.left, a);
		if (!a[0]) return left + 1;
		int right = TreeDepth2(root.right, a);
		if (!a[0]) return right + 1;
		int diff = Math.abs(left - right);
		if (diff > 1) a[0] = false;
		return left > right ? left + 1 : right + 1;
	}
	public static boolean isBalanceBiTree2(BiTree root) {
      
		boolean a[] = new boolean[1];
		a[0] = true;
		TreeDepth2(root, a);
		return a[0];
	}
	public static void main(String[] args) {
     
		BiTree root = new BiTree(1);
		BiTree node1 = new BiTree(2);
		BiTree node2 = new BiTree(3);
		BiTree node3 = new BiTree(4);
		BiTree node4 = new BiTree(5);
		BiTree node5 = new BiTree(6);
		root.left = node1;
		root.right = node2;
		node1.left = node3;
		node1.right = node4;
		node3.left = node5;
		System.out.println(isBalanceBiTree(root));
	}
	static class BiTree{
     
		BiTree left;
		int data;
		BiTree right;
		public BiTree(int data) {
     
			this.data = data;
		}
	}
}

18.二叉树的中序遍历

题目描述:给定一个二叉树,返回它的中序遍历。
思路:方法1,递归解法。方法2,非递归解法。

public static void Inorder(BiTree root) {
       // 递归解法
	if (root == null) return;
	Inorder(root.left);
	System.out.print(root.data + " ");
	Inorder(root.right);
}
public static void Inorder2(BiTree root) {
       // 非递归解法
	if (root == null) return;
	Stack<BiTree> s = new Stack<BiTree>();
	BiTree p = root;
	while (p != null || !s.isEmpty()) {
     
		if (p != null) {
     
			s.push(p);
			p = p.left;
		} else {
     
			BiTree b = s.pop();
			System.out.print(b.data + " ");
			p = p.right;
		}
		
	}
}

19.只出现一次的数字

题目描述:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现一次的元素。
思路:方法1,排序。方法2,位运算之异或运算。异或有个性质就是相同的两个数异或结果为0,0与非0异或为非零,因此,可将数组中的数累次进行异或,最终结果就是那个只出现一次的元素(注:异或满足交换律)。

public static int getOneNum(int a[]) {
       // 排序解法
	Arrays.sort(a);
	for (int i = 0 ; i < a.length - 2 ; i += 2) {
     
		if (a[i] != a[i+1]) return a[i];
	}
	return 0;
}
public static int getOneNum2(int a[]) {
       // 位运算  异或
	int ans = 0;
	for (int i = 0 ; i < a.length ; i++) {
     
		ans ^= a[i];
	}
	return ans;
}

20.扩展“只出现一次的数字”

题目描述:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
思路:方法1,排序。
方法2,位运算(方向同样是消掉3次重复的数据)。考虑到一个int型数据是32位,对数组中每个元素的每一个“位”进行统计(统计1的次数),那么出现三次的元素各“位”统计下来必定是0或者3,所有出现三次的数字各“位”统计下来必定是3的倍数。比如说[10,10,8,10,2,2,2],(10)(10)=(0000 0000 0000 0000 0000 0000 0000 1010)(2),我们从第0-31位分别统计每位1出现的次数,这里10出现三次,那么低位第2位1出现的次数就为三,然后2也出现三次,低2位贡献3个1,而8出现一次,低二位不贡献1,因此1的总个数模三后就是8对应位1的个数。这里注意统计时右移了i位,因此求返回结果时需要还原即左移i位,进行累次或运算。
方法3,位运算(方向是设计一个逻辑运算,类似异或运算,但与之不同,该运算是三次相同则消零),但与法2不同。法2是将各位的1统计出来,而这里是将各位的运算结果通过两个位(高位a,低位b)来存储起来。且这两个位是逢3消为0,即00->01->10->00。先写真值表:

a输入 b输入 输入i a输出 b输出
0 0 0 0 0
0 1 0 0 1
1 0 0 1 0
0 0 1 0 1
0 1 1 1 0
1 0 1 0 0

可推导出b和a的逻辑表达式:bout=~ ain & (bin ^ iin);aout=~ bout & (ain ^ iin)。

public static int getOneNum(int a[]) {
       // 排序解法
	Arrays.sort(a);
	for (int i = 0 ; i < a.length - 3 ; i += 3) {
     
		if (a[i] != a[i+1] || a[i] != a[i+2]) return a[i];
	}
	return a[a.length - 1];
}
public static int getOneNum2(int a[]) {
       // 位运算 通用解法O(32N),可对n=3,4,5,6...适用
	int ans = 0;
	for (int i = 0 ; i < 32 ; i++) {
     
		int count = 0;
		for (int j : a) {
       // 统计每一位1的个数
			count += j >> i & 1;
		}
		ans |= count % 3 << i;
	}
	return ans;
}
public static int getOneNum3(int a[]) {
       // 位运算 2位表示1的个数,00 01 10 00第三个1会清零O(N)
	int high = 0, low = 0;
	for (int i : a) {
     
		low = ~high & (low ^ i);  // 可根据真值表推出逻辑表达式
		high = ~low & (high ^ i);
	}
	return low;
}

21.扩展“只出现一次的数字”

题目描述:一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。如输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]。(来源:LeetCode面试题56-1)
思路:和19不同,这里要求出两个出现一次的数字,这里关键在于利用异或的性质,先将数组所有数异或出来,结果会得到两个出现一次的数的异或。考虑二进制,这两个数异或出来的如果是1,说明对应位不同;如果是0,说明对应位相同。因此我们可以找出异或结果右边开始第一个为1的位置,然后将原来的两个数分成两组,这样就可以分别求出题目所要求的两个数。

public int[] singleNumbers(int[] nums) {
     
    int ans[] = new int[2];
    int a = 0;
    for (int i = 0 ; i < nums.length ; ++i){
       // 所有数的异或
        a ^= nums[i];
    }
    int one = 0;
    for (int i = 0 ; i < 32 ; ++i, ++one){
       // 从右边开始找a的第一个为1的位置
        if (((a >> one) & 1) == 1) break;
    }
    for (int i = 0 ; i < nums.length ; ++i){
       // 分组求解
        if (((nums[i] >> one) & 1) == 1) ans[0] ^= nums[i];
        else ans[1] ^= nums[i];
    }
    return ans;
}

你可能感兴趣的:(刷题,java,算法)