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

1.合并两个有序的单链表

题目描述:给定两个有序单链表的头结点head1和head2,请合并两个有序链表,合并后的链表依然有序,并返回合并后链表的头结点。如0->2->3->7->null,1->3->5->7->9->null。合并后:0->1->2->3->3->5->7->7->9->null
思路:双指针遍历,取结点值较小者链接到新的单链表后面,剩余的接上。而合并三个有序的单链表,只需调用两次合并方法即可。
扩展:如果是无序的呢?可以先将他们排成有序链表再合并

public static Node merge(Node node1, Node node2) {  // 合并两个有序链表
	if (node1 == null || node2 == null) return node1 == null ? node2 : node1;
	Node head = node1.data > node2.data ? node2 : node1;
	Node m = node1.data > node2.data ? node1 : node2;
	Node n = node1.data > node2.data ? node2.next : node1.next;
	Node p = head;
	while (p.next != null) {
		if (m.data > n.data) {
			p.next = n;
			n = n.next;
			p = p.next;
		}
		else {
			p.next = m;
			m = m.next;
			p = p.next;
		}
	}
	p.next = m == null ? n : m;  // 剩余的接上
	return head;
}
public static Node merge3(Node node1, Node node2, Node node3) {  // 合并三个有序链表
	return merge(merge(node1, node2), node3);
}
static class Node{
	int data;
	Node next;
	Node(int data){
		this.data = data;
	}
}

2.删除排序链表的重复元素

题目描述:给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。如1->1->2->null,输出1->2->null
思路:递归方式。“递”的过程一直到最后一个结点,“归”的过程,先将返回的指针链接到上一个结点(保证不断链),然后判断上一个结点的值和返回结点的值,相等则返回后一个结点,否则返回前一个。
非递归方式。利用两个指针,一个指向新链表的最后一个结点,另一个用作遍历指针,将与最后一个结点值不相等的结点链接上去。

public static Node deleteSameNode(Node head) {  // 递归删除重复结点
	if (head == null || head.next == null) return head;
	head.next = deleteSameNode(head.next);
	return head.data == head.next.data ? head.next : head;
}
public static Node deleteSameNode2(Node head) {  // 非递归删除重复结点
	if (head == null || head.next == null) return head;
	Node p = head;
	Node q = head.next;
	while(q != null) {
		if (q.data == p.data) q = q.next;
		p.next = q;
		p = p.next;
		q = q.next;
	}
	
	return head;
}

3.环形链表

题目描述:给定一个链表,判断链表是否有环。如3->2->0->-4,-4再指向2那个结点。输出:有环。
思路:方法一,给结点增加一个访问标志位。
方法二,快慢指针。如果存在环,快指针必定能追上慢指针。

public static boolean isCircle(Node head) {  // 结点增加访问标志判断是否有环
	if (head == null || head.next == null) return false;
	while (head != null) {
		if (head.visited) return true;
		head.visited = true;
		head = head.next;
	}
	return false;
}
public static boolean isCircle2(Node head) {  // 快慢指针判断是否有环,如果有环,2倍速总能追上1倍速,3,4,5...倍速一样的
	if (head == null || head.next == null) return false;
	Node slow = head;
	Node fast = head.next;
	while (slow != null && fast != null && fast.next != null) {
		if (slow == fast) return true;
		slow = slow.next;
		fast = fast.next.next;
	}
	return false;
}
static class Node{
	int data;
	Node next;
	boolean visited;
	Node(int data){
		this.data = data;
		visited = false;
	}
}

4.链表的中间结点

题目描述:给定一个带有头结点head的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。如1->2->3->4->5->null,返回3那个结点。
思路:快慢指针。

public static Node getMiddleNode (Node head) { // 快慢指针解法
	if (head == null) return head;
	Node slow = head;
	Node fast = head;
	while (fast != null && fast.next != null) {
		slow = slow.next;
		fast = fast.next.next;
	}
	return slow;
}

5.两数相加

题目描述:给出两个非空的链表用来表示两个非负的整数。其中,它们各自的位数是按照逆序的方式存储的,并且它们的每个结点只能存储一位数字。如果我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。除0之外,这两个数都不会以0开头。如2->4->3->null,5->6->4->null。返回7->0->8->null
思路:方法1:对位相加,保留进位。若有多余的结点,将其与进位相加直至没有多余结点。
方法2:不对齐则补零。其实和方法1差不多,只是实现有些区别。

public static Node sum2(Node node1, Node node2) {
	Node headPre = new Node(-1);
	Node p = headPre;
	int data, over = 0;
	while (node1 != null && node2 != null) {
		data = (node1.data + node2.data + over) % 10;
		over = (node1.data + node2.data + over) / 10;
		p.next = new Node(data);
		p = p.next;
		node1 = node1.next;
		node2 = node2.next;
	}
	while(node1 != null) {
		data = (node1.data + over) % 10;
		over = (node1.data + over) / 10;
		p.next = new Node(data);
		p = p.next;
		node1 = node1.next;
	}
	while(node2 != null) {
		data = (node2.data + over) % 10;
		over = (node2.data + over) / 10;
		p.next = new Node(data);
		p = p.next;
		node2 = node2.next;
	}
	if (over != 0) p.next = new Node(over);
	return headPre.next;
}
public static Node sum22(Node m1, Node n1) {  // 不对齐补零  十进制相加进位只能是1,当然更广泛的进位可以如下计算。这里可以简化为1
	Node headPre = new Node(-1);
	Node p = headPre;
	int data, over = 0;
	while(m1 != null || n1 != null) {
		data = 0;
		if (m1 != null) {
			data += m1.data;
			m1 = m1.next;
		}
		if (n1 != null) {
			data += n1.data;
			n1 = n1.next;
		}
		data += over;
		over = data / 10;
		data = data % 10;
		p.next = new Node(data);
		p = p.next;
	}
	if (over > 0) p.next = new Node(over);
	return headPre.next;
}

6.删除链表的倒数第N个结点

题目描述:给定一个链表,删除链表的倒数第n个结点,并且返回链表的头结点。如1->2->3->4->5->null,n=5,返回2那个结点。
思路:方法1:将链表复制到列表(或数组)里面,利用随机访问特性进行删除。
方法2:双指针解法。先将两个指针设置间隔为n,再同步遍历直至后一个指针先到达最后一个结点为止。

public static Node deleteLastXNode(Node head, int n) {
	Node p = head;
	List<Node> nodes = new ArrayList<Node>();
	while(p != null) {
		nodes.add(p);
		p = p.next;
	}
	if (n > nodes.size() || n <= 0) return head;
	if (n == nodes.size()) return head.next;
	Node temp = nodes.get(nodes.size() - n - 1);
	temp.next = temp.next.next;
	return head;
}
public static Node deleteLastXNode2(Node head, int n) {  // 双指针隔n解法
	if (n <= 0) return head;
	Node headPre = new Node(0);
	headPre.next = head;
	Node p = headPre;
	Node q = p;
	for (int i = 1 ; i <= n; i++) {
		q = q.next;
		if (q == null) return head;  // n大于链表长度,直接返回头结点
	}
	while (q.next != null) {
		p = p.next;
		q = q.next;
	}
	p.next = p.next.next;
	return headPre.next;
}

7.合并K个排序列表

题目描述:合并k个排序列表,返回合并后的排序链表。如1->4->5->null,1->3->4->null,2->6->null,返回1->1->2->3->4->4->5->6->null。
思路:利用最小堆优先队列。把链表头结点都放入队列,然后出队一个结点(保证了是队列中值最小的结点),将其链接到新链表后面,然后将该结点的下一个结点入队。重复此步骤直至队列为空。

public static Node merge(Node [] nodes) {
	if (nodes.length == 0) return null;
	PriorityQueue<Node> n = new PriorityQueue<>(nodes.length, new Comparator<Node>() {
		@Override
		public int compare(Node o1, Node o2) {
			return o1.data - o2.data;
		}
	});
	for(Node node : nodes) {
		n.offer(node);
	}
	Node head = n.poll();
	n.offer(head.next);  // offer()入队
	Node p = head;
	while (n.peek() != null) {  // peek()查看队列中队首结点。
		p.next = n.poll();  // poll()出队首结点。
		p = p.next;
		if (p.next != null)
		n.offer(p.next);
	}
	return head;
}

8.子数组的最大累加和

题目描述:给定一个数组arr,返回子数组的最大累加和。如:arr=[1,-2,3,5,-2,6,-1],所有子数组中,[3,5,-2,6]可以累加出最大的和12,故返回12。
思路:注意到如果某个子数组的累加和小于等于0,可以直接不考虑这段子数组。

public static int getMaxCumSum(int a[]) {
	int sum = 0, max = 0;
	for (int i = 0 ; i < a.length ; i++) {
		sum += a[i];
		if (sum <= 0) sum = 0;
		max = sum > max ? sum : max;
	}
	return max;
}

9.子数组的最大累乘积

题目描述:给定一个double类型的数组arr,其中的元素可正可负可零,返回子数组累乘积的最大乘积。例如arr=[-2.5,4,0,3,0.5,8,-1],子数组[3,0.5,8]累乘可以获得最大的乘积12,所以返回12。
思路:考虑以arr[i]结尾的子数组(不需要考虑整个子数组具体什么样),它的最大累乘积只有三种情况:1、max * arr[i](max是以arr[i-1]结尾的子数组的最大累乘积);2、min * arr[i](min是以arr[i-1]结尾的子数组的最小累乘积);3、arr[i]
注:顺便可求最小累乘积

public static double getMaxCumProduct(double a[]) {
	if (a == null || a.length == 0) return 0;
	double res = a[0];
	double min = a[0];
	double max = a[0];
	for (int i = 1 ; i < a.length ; i++) {
		double t1 = max * a[i];
		double t2 = min * a[i];
		max = Math.max(Math.max(t1, t2), a[i]);
		min = Math.min(Math.min(t1, t2), a[i]);
		res = Math.max(res, max);
		//res = Math.min(res, min);  // 最小累乘积
	}
	return res;
}

10.子集

题目描述:给定一组不含重复元素的整数数组nums,返回该数组所有可能的子集(幂集)。如[1,2,3],返回[[],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]。
思路:注意到幂集个数为2n,n为数组元素个数。利用0~2n-1对应的二进制中1的位置来取数组中的元素。比如nums = [1,2,3],0的二进制000,什么都不取,为空子集;3的二进制011,取第二和第三个元素,子集为[2,3];7的二进制111,三个元素都取,子集为[1,2,3]。
方法1是将二进制转成字符串处理,方法二是通过位运算处理。

public static String getDec2Bin(int a, int n) {  //补零
	String s = "";
	while (a != 0) {
		s = a % 2 + s;
		a /= 2;
	}
	while (s.length() < n) {
		s  = "0" + s;
	}
	return s;
}
public static List<List<Integer>> getSubCollection(int a[]){
	List<List<Integer>> lists = new ArrayList<List<Integer>>();
	for (int len = 0 ; len < Math.pow(2, a.length) ; len++) {  // 对每个len转成二进制
		String s = getDec2Bin(len, a.length);
		List<Integer> list = new ArrayList<Integer>();
		for (int i = 0; i < s.length() ; i++) {
			if (s.charAt(i) == '1') {
				list.add(a[i]);
			}
		}
		lists.add(list);
	}
	return lists;
}
public static List<List<Integer>> getSubCollection2(int a[]){  // 位运算
	List<List<Integer>> lists = new ArrayList<List<Integer>>();
	lists.add(new ArrayList<Integer>());  // 空集
	int hash = 1;
	while (hash < Math.pow(2, a.length)) {  // 1-7   有7种可能
		List<Integer> list = new ArrayList<Integer>();
		for (int k = 0 ; k < a.length ; k++) {  // 每种都要对原数组中的数进行测试,看看hash值的二进制形式有几个1(与测试)
			int c = 1<<k;  // c依次为001 010 100,k值为左移位数,也是将要从原数组a中某个下标为k的数添加到list中。
			if ((c & hash) != 0) {  // 
				list.add(a[k]);
			}
		}
		lists.add(list);
		hash++;
	}
	return lists;
}
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值