数据结构与算法_动态数组及单向链表练习题

88合并两个有序数组
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]

解题思路:

  1. 因为两个数组都是有序的,定义index1的起始位置nums1的数组长度m-1,index2的起始位置nums2的数组长度为n-1,index的起始位置为最终得到的nums1的长度m+n-1;
  2. 将数组nums1和数组nums2都从后到前遍历,当nums1[index]>nums2[index2],将nums1[index]=nums1[index1],再将指针index和指针index1前移;
  3. 否则,将nums1[index]=nums2[index2],并且指针前移;
  4. 当数组nums1先移动完成,即index1<0时,将nums1[i]=nums2[i],i<=index2;
  5. 当数组nums2先移动完成时,nums1的原先位置的顺序不变即可。

代码:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int index1 = m - 1;
		int index2 = n - 1;
		int index = m + n - 1;
		while (true) {
			if (index1 < 0 || index2 < 0) {
				break;
			}
			if (nums1[index1] > nums2[index2]) {
				nums1[index--] = nums1[index1--];
			} else {
				nums1[index--] = nums2[index2--];
			}
		}
		if (index2 >= 0) {
			for (int i = 0; i <= index2; i++) {
				nums1[i] = nums2[i];
			}
		}
    }
}

35插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。

示例 1:
输入: [1,3,5,6], 5
输出: 2

示例 2:
输入: [1,3,5,6], 2
输出: 1

示例 3:
输入: [1,3,5,6], 7
输出: 4

示例 4:
输入: [1,3,5,6], 0
输出: 0

解题思路:用二分查找法查找。

  1. 定义索引0为low,定义索引nums.length-1为high,定义mid=(low+high)/2为中间值;
  2. while循环解决,条件为low<=high;
  3. 当给定值大于中间值,low=mid+1;
  4. 当给定值小于中间值,high=mid-1;l
  5. 当给定值小于中间值,return low;
  6. 如果循环结束没有找到,则返回low为插入地方。

代码

class Solution {
    public int searchInsert(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
			return 0;
		}
		int low = 0;
		int high = nums.length - 1;
		int mid = (low + high) / 2;
		if (nums[low] == target) {
			return low;
		}
		if (nums[high] == target) {
			return high;
		}
		while (low <= high) {
			if (nums[mid] == target) {
				return mid;
			}
			if (nums[mid] < target) {
				low = mid + 1;
			} else {
				high = mid - 1;
			}
			mid = (low + high) / 2;
		}
		return low;
    }
}

66加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。

示例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。

解题思路:

  1. 定义一个值代表进位carry,num代表当前位的值;
  2. 将数组从后向前遍历,给最后一位加1,即num=digits[length-1]+1;
  3. 并将num%10的值赋给当前,digits[i]=num%10;
  4. carry=num/10;当进位carry==0是时,跳出循环;
  5. 循环结束,当carry==1时,从新定义新数组,且长度时原来的数组长度+1;将1赋给新数组的第一位。
class Solution {
    public int[] plusOne(int[] digits) {
        int carry = 1;
		int num = 0;
		for (int i = digits.length - 1; i >= 0; i--) {
			num = digits[i] + carry;
			digits[i] = num % 10;
			carry = num / 10;
			if (carry == 0) {
				break;
			}
		}
		if (carry == 1) {
			int[] arr = new int[digits.length + 1];
			arr[0] = 1;
			return arr;
		} else {
			return digits;
		}
    }
}

20.有效的括号
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。

注意空字符串可被认为是有效字符串。

示例 1:
输入: “()”
输出: true

示例 2:
输入: “()[]{}”
输出: true

示例 3:
输入: “(]”
输出: false

示例 4:
输入: “([)]”
输出: false

示例 5:
输入: “{[]}”
输出: true

解题思路:用栈去查找
当进栈元素和栈顶-1或者-2相等,则出栈栈顶元素,完成一组匹配;
最后当栈为空,则完全匹配,返回true;
否则不匹配,返回false。

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<Character>();
		for (int i = 0; i < s.length(); i++) {
			char c=s.charAt(i);
			if(stack.isEmpty()){
				stack.push(c);
			}else{
				char topc=stack.peek();
				if(topc-c==-1||topc-c==-2){
					stack.pop();
				}else{
					stack.push(c);
				}
			}
		}
		return stack.isEmpty();
    }
}

240搜索二维矩阵II
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:

每行的元素从左到右升序排列。
每列的元素从上到下升序排列。

示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]

给定 target = 5,返回 true。
给定 target = 20,返回 false。

解题思路:
从左下开始查找,小于该坐标元素值,朝上走,大于该坐标元素值,超左走;
bu最后判定横向不大于matrix[0].length-1并且不小于0,纵向不大于matrix.length-1并且不小于0作为超处数组的限定条件;
找到了,返回true,如果遍历完整个数组都没有找到,返回false。

代码

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        if(matrix==null||matrix.length==0||matrix[0].length==0){
			return false;
		}
		int row=matrix.length;
		int col=matrix[0].length;
		int x=row-1;
		int y=0;
		while(true){
			if(x<0||y>=col){
				return false;
			}
			if(matrix[x][y]<target){
				y++;
			}else if(matrix[x][y]>target){
				x--;
			}else{
				return true;
			}
			
		}
    }
}

209长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。

示例:
输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。

进阶:
如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。

解题思路:用滑窗机制解决。

  1. 定义len存放最短长度,sum存放滑窗的元素的总和,两个指针对应滑窗的头和尾;
  2. 当滑窗内的sun小于给定值s,则尾指针后移;
  3. 当滑窗内的sum大于或等于给定值s,头指针后移,并把当前的滑窗长度记录;
  4. 当滑窗尾指针移到最后一个元素,即到length-1时,返回长度len。
public class Solution {
	public int minSubArrayLen(int s, int[] nums) {
		if (nums == null || nums.length == 0) {
			return 0;
		}
		int len = 0;
		int i = 0;
		int sum = 0;
		for (int j = 0; j < nums.length; j++) {
			sum += nums[j];
			while (sum >= s) {
				len = len == 0 ? j - i + 1 : Math.min(len, j - i + 1);
				sum -= nums[i++];
			}
		}
		return len;
	}
}

54螺旋矩阵
给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。

示例 1:
输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]

示例 2:
输入:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]

解题思路:
在这里插入图片描述
如图,是数字的转动方向。
向右走:detX=0 delY=1
向下走:detX=1 delY=0
向左走:detX=0 delY=-1
向上走:detX=-1 delY=0
即:
deltX={0,1,0,-1}
deltY={1,0,-1,0}
因此,确定了转动方向的变化量,再限制不能超出的条件和不能转到已经走过的值;
即,规定一个等大的boolean类型的数组,当走过尾true,没走过为false;
还有下一次不能走出数组,横向小于matrix.length-1并且不小于0,纵向小于matrix[0].length-1并且不小于0即可。

代码:

public class Solution{

	public List<Integer> spiralOrder(int[][] matrix) {
		ArrayList<Integer> list = new ArrayList<Integer>();
		if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
			return list;
		}
		int x = 0;
		int y = 0;
		int R = matrix.length;
		int C = matrix[0].length;
		boolean[][] visited = new boolean[R][C];
		int[] deltX = { 0, 1, 0, -1 };
		int[] deltY = { 1, 0, -1, 0 };
		int dir = 0;
		for (int i = 0; i < R * C; i++) {
			list.add(matrix[x][y]);
			visited[x][y] = true;
			int nx = x + deltX[dir];
			int ny = y + deltY[dir];
			if (nx >= 0 && nx < R && ny >= 0 && ny < C && !visited[nx][ny]) {
				x = nx;
				y = ny;
			} else {
				dir = (dir + 1) % 4;
				x += deltX[dir];
				y += deltY[dir];
			}
		}
		return list;
	}
}

2.两数相加
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

解题思路:用头指针head,尾指针rear和进位carry完成。

  1. 定义一个新链表的头指针尾head,尾指针指向头指针的位置,定义变量carry 为进位。
  2. 循环完成,当l1!=0时加上l1;当l2不为0时加上l2在加上进位carry;
  3. 将总和对10取余赋给rear.next;并且总和除以10赋给carry;
    4.循环完成后,返回head.next,因为head不存有效元素。
public class Solution2 {
	public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
		ListNode head = new ListNode(0);
		ListNode rear = head;
		int carry = 0;
		while (true) {
			if (l1 == null && l2 == null && carry == 0) {
				break;
			}
			int num = (l1 == null ? 0 : l1.val) + (l2 == null ? 0 : l2.val) + carry;
			ListNode n = new ListNode(num % 10);
			rear.next = n;
			rear = n;
			carry = num / 10;
			l1 = l1 == null ? null : l1.next;
			l2 = l2 == null ? null : l2.next;
		}
		return head.next;
	}
}

19.删除链表的倒数第N个节点
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.

解题思路:
双指针,第一个指针先走,走过n个元素后,然后两个指针一起走。当第一个指针走到为空时停下,即第二个指针指向的节点的下一个节点为删除的节点。

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
		if (head == null || head.next == null) {
			return null;
		}
		ListNode r = head;
		ListNode l = head;
		for (int i = 0; i < n; i++) {
			r = r.next;
		}
		if (r == null) {
			return head.next;
		}
		while (r.next != null) {
			r = r.next;
			l = l.next;
		}
		l.next = l.next.next;
		return head;
	}
}

21.合并两个有序链表
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

解题思路:
双指针。使指针p1,p2分别指向节点l1,l2,即p1,p2指向两个链表的头节点;定义新链表头节点为head,并且p指向head;
将两个指针从头往后遍历.
如果p1.val<=p2.val,p.next = p1;p1 = p1.next;
如果p1.val>p2.val,p.next = p2;p2 = p2.next;
如果p1!=null,p2= =null;p.next = p1;break;
如果p1= =null,p2!=null;p.next = p2;break;
如果p1= =null,p2= =null;break;

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
      ListNode head = new ListNode(0);
		ListNode p = head;
		ListNode p1 = l1;
		ListNode p2 = l2;
		while (true) {
			if (p1 == null && p2 == null) {
				break;
			}
			if (p1 != null && p2 == null) {
				p.next = p1;
				break;
			} else if (p1 == null && p2 != null) {
				p.next = p2;
				break;
			} else if (p1.val <= p2.val) {
				p.next = p1;
				p1 = p1.next;
			} else {
				p.next = p2;
				p2 = p2.next;
			}
			p = p.next;
		}
		return head.next;
    }
}

328.奇偶链表
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

示例 1:
输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL

示例 2:
输入: 2->1->3->5->6->4->7->NULL
输出: 2->3->6->7->1->5->4->NULL

解题思路:
双指针,一个从奇数位走,另一个从偶数位走,当两个都走完时,从奇数位的最后一位指向偶数位的第一位,然后返回链表即可。

class Solution {
    public ListNode oddEvenList(ListNode head) {
        if (head == null || head.next == null) {
			return head;
		}
		ListNode ji = head;
		ListNode ou = head.next;
		ListNode ouStart = ou;
		while (true) {
			if (ou == null || ou.next == null) {
				break;
			}
			ji.next = ji.next.next;
			ou.next = ou.next.next;
			ji = ji.next;
			ou = ou.next;
		}
		ji.next = ouStart;
		return head;
    }
}

141.环形链表
给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
在这里插入图片描述

示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
在这里插入图片描述

示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
在这里插入图片描述

解题思路:
双指针,一个指针一次走两个节点,另一个指针一次走一个节点。
当第一个节点走到空,则没有环;
当第一个节点追上第二个节点,则是第一个节点过环后追上,即有环。

public class Solution {
    public boolean hasCycle(ListNode head) {
       if (head == null || head.next == null) {
			return false;
		}
		ListNode fast = head;
		ListNode slow = head;
		while (true) {
			if (fast == null || fast.next == null) {
				return false;
			}
			slow = slow.next;
			fast = fast.next.next;
			if (slow == fast) {
				return true;
			}
		} 
    }
}

142.环形链表II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
在这里插入图片描述

示例 2:
输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。
在这里插入图片描述

示例 3:
输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。
在这里插入图片描述

解题思路:
和上一个雷同,但是走的方法不同。
一个节点先走一个,另一个节点从头走一个,并比较,当两个走的次数相同碰到则不记;
第一个再原来的基础上走一个,另一个从头走两个,当两个走的次数相同碰到则不记;
。。。。。
以此类推,当两个指针走的次数不同,但是碰到了,则有环,并且入环的位值是第二个节点的位置。其他情况下均无环。

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null) {
			return null;
		}
		ListNode fast=head;
		ListNode slow=head;
		int step=0;
		while(fast!=null){
			fast=fast.next;
			step++;
			slow=head;
			for (int i = 1; i <= step; i++) {
				if(slow==fast&&step!=i){
					return slow;
				}
				slow=slow.next;
			}
		}
		return null;   
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值