文章目录
-
待办项:
-
1. 单链表:创建链表,查找某个节点,删除某个节点,新增一个节点,插入一个节点,是否有环,两个链表相交的起始节点,反转链表。
-
2. 二叉树:创建二叉树,前序遍历/中序遍历/后续遍历,找出二叉树的最大深度。
参考:二叉树的创建及遍历方法 -
3. 位运算:
-
4. 字符串:
-
参考博客:leetCode热题
1. 把两个有序链表整合成一个新的有序列表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
public class MergeTwoNodeTest_1 {
public static class CommonNode<V> {
public V value;
public CommonNode next;
}
/**
* 题目:将两个有序的链表,整合出一个有序的链表,从小到大
*/
public static void main(String[] args) {
CommonNode commonNode1 = new CommonNode<Integer>();
commonNode1.value = 1;
CommonNode commonNode2 = new CommonNode<Integer>();
commonNode2.value = 2;
commonNode1.next = commonNode2;
CommonNode commonNode3 = new CommonNode<Integer>();
commonNode3.value = 3;
CommonNode commonNode4 = new CommonNode<Integer>();
commonNode4.value = 4;
commonNode3.next = commonNode4;
CommonNode mergeNode = mergeTwoNodeList(commonNode1, commonNode3);
while (mergeNode != null) {
System.out.println(mergeNode.value);
mergeNode = mergeNode.next;
}
}
private static CommonNode mergeTwoNodeList(CommonNode commonNode1, CommonNode commonNode2) {
CommonNode headNode = new CommonNode();
headNode.value = -1;
CommonNode preNode = headNode;
while (commonNode1 != null && commonNode2 != null) {
if ((Integer)commonNode1.value < (Integer)commonNode2.value){
preNode.next = commonNode1;
commonNode1 = commonNode1.next;
} else {
preNode.next = commonNode2;
commonNode2 = commonNode2.next;
}
preNode = preNode.next;
}
if (commonNode1 == null) {
preNode.next = commonNode2;
} else {
preNode.next = commonNode1;
}
return headNode.next;
}
}
2. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那两个整数,并返回它们的数组下标。每种输入只会对应一个答案。但是,数组中同一个元素在答案里不会重复出现。
可以按任意顺序返回答案。
class Solution {
public int[] twoSum(int[] nums, int target) {
//Map<Integer, Integer> map = new HashMap<>();
Map<Integer,Integer>map =new HashMap<>();
for(int i = 0; i< nums.length; i++) {
if(map.containsKey(target - nums[i])) {
return new int[] {map.get(target-nums[i]),i};
}
map.put(nums[i], i);
}
return new int[0];
}
}
3. 有效括号的字符串
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
示例 4:
输入:s = “([)]”
输出:false
class Solution {
public boolean isValid(String s) {
int n = s.length();
if (n % 2 == 1) {
return false;
}
Map<Character, Character> pairs = new HashMap<Character, Character>() {{
put(')', '(');
put(']', '[');
put('}', '{');
}};
Deque<Character> stack = new LinkedList<Character>();
for (int i = 0; i < n; i++) {
char ch = s.charAt(i);
if (pairs.containsKey(ch)) {
//栈顶元素不是当前元素的左括号
if (stack.isEmpty() || stack.peek() != pairs.get(ch)) {
return false;
}
//栈顶元素是当前元素的左括号,出栈
stack.pop();
} else {
//入栈
stack.push(ch);
}
}
return stack.isEmpty();
}
}
4. 爬楼梯
假设正在爬楼梯。需要 n 阶才能到达楼顶。每次可以爬 1 或 2 个台阶。有多少种不同的方法可以爬到楼顶?
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1 阶 + 1 阶
2 阶
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1 阶 + 1 阶 + 1 阶
1 阶 + 2 阶
2 阶 + 1 阶
思路:想达到第n阶,要么是从第n-1阶爬1次1阶上去,要么是从第n-2阶爬一次2阶上去,所以有 solution(n) = solution(n-1) + solution(n-2)
- 方案1:动态归化法
public class Solution {
public int climbStairs(int n) {
if(n==0)return 0;
if(n==1)return 1;
int []a=new int [n+1];
a[1]=1;
a[2]=2;
for(int i=3;i<=n;i++){
a[i]=a[i-1]+a[i-2];
}
return a[n];
}
}
- 方案2:通过滑动数组进行动态规划
public class Solution {
public int climbStairs(int n) {
int p = 0, q = 0, r = 1;
for (int i = 1; i <= n; ++i) {
p = q;
q = r;
r = p + q;
}
return r;
}
};
5、最大子数组和
给你一个整数数组 nums ,请找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
public class MaxSumOfSubListTest_2 {
public static void main(String[] args) {
int[] nums = new int[]{1, -2, 3, 4, 5};
int max = maxSubArray(nums);
}
public static int maxSubArray(int[] nums) {
int pre = 0;
int preMax = nums[0];
for (int i = 0; i < nums.length; i++) {
//比较当前值、当前值+前面连续子数组的最大和值,取两者的最大作为含当前值的最大最大和值
pre = Math.max(pre + nums[i], nums[i]);
//比较当前值的最大最大和值、前面连续子数组的最大和值(不含当前值)
preMax = Math.max(pre, preMax);
System.out.println("" + (pre == preMax) + ", pre=" + pre + ", preMax=" + preMax);
}
return preMax;
}
}
6. 链表是否有环
给出一个链表的头节点 head ,判断链表中是否有环。
- 方案1:通过set判断是否有重复(普通人)
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> set = new HashSet<ListNode>();
while (head != null) {
if (set.contains(head)) {
return true;
}else set.add(head);
head = head.next;
}
return false;
}
}
- 方案2:快慢指针法
思路:同一起点但是不一样速度,最后如果能碰面说明有环。
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast!=null && fast.next!=null) {
slow = slow.next;
fast = fast.next.next;
if(fast==slow){
return true;
}
}
return false;
}
}
7、无环的相交链表
编写一个程序,找到两个单链表相交的起始节点。
注意:如果两个链表没有交点,则返回 null。不可改变两个链表原有结构。整个链表没有环。尽量满足 O(n) 时间复杂度, O(1) 内存。
思路1:假设链表A由x+y两段长度组成,链表B由z+y两段长度组成,y是两个链路的公共链路,则链路x+y+z的长度 等于链路z+y+x的长度。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA==null||headB==null){
return null;
}
ListNode A1=headA;
ListNode A2=headB;
//A1、A2走过的长度为x+y+z时,A1恰好等于A2
while(A1!=A2){
//链路A1最终走过的长度是x+y+z
if(A1!=null){
A1=A1.next;
}else{
A1=headB;
}
//链路A2最终走过的长度是z+y+x
if(A2!=null){
A2=A2.next;
}else{
A2=headA;
}
}
return A1;
}
}
思路2:通过哈希表的contain来判断(空间复杂度增大,不写了)
8、反转单链表
给出单链表的头节点 head ,请反转链表,并返回反转后的链表头结点。
思路:参考博客——详解反转链表
public class ReverseSingleLink {
public static class CommonNode<V> {
public V value;
public CommonNode next;
public CommonNode() {
}
public CommonNode(V value) {
this.value = value;
}
public CommonNode(V value, CommonNode next) {
this.value = value;
this.next = next;
}
}
/**
* 给出单链表的头节点 head ,请反转链表,并返回反转后的链表。
*/
public static void main(String[] args) {
CommonNode commonNode3 = new CommonNode<Integer>(3);
CommonNode commonNode2 = new CommonNode<Integer>(2, commonNode3);
CommonNode commonNode1 = new CommonNode<Integer>(1, commonNode2);
CommonNode headNode = new CommonNode<Integer>(0, commonNode1);
CommonNode reverseHeadNode = reverseLink(headNode);
while (reverseHeadNode != null) {
System.out.println(reverseHeadNode.value);
reverseHeadNode = reverseHeadNode.next;
}
}
private static CommonNode reverseLink(CommonNode head) {
// 如果链表为null 直接返回null
if (head == null) {
return null;
}
// 如果链表只有一个节点,直接返回头节点
if (head.next == null) {
return head;
}
CommonNode pre = null;
CommonNode cur = head;
CommonNode temp = null;
while (cur != null) {
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
9、最长回文子串
给出一个字符串,请输出该字符串的最长回文子串。回文串是正读和反读一样的字符串。
思路1:中心扩展算法(参考博客——如何找到字符串中的最长回文子串?)
public String longestPalindrome(String s) {
if (s == null || s.length() == 0) {
return "";
}
int maxBeginIndex = 0;
int maxLen = 0;
for (int cur = 0; cur < s.length(); cur++) {
//当前批次回文串的长度
int len = 1;
//定义两个指针从cur开始
int left = cur-1;
int right = cur +1 ;
//left一直朝左遍历,直到到达数组左边界或者不等于cur
while (left >= 0 && s.charAt(left) == s.charAt(cur)) {
left--;
len++;
}
//right一直朝右遍历,直到到达数组右边界或者不等于cur
while (right < s.length() && s.charAt(cur) == s.charAt(right)) {
right++;
len++;
}
//判断left处和right处是否相等
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
left--;
right++;
len = len + 2;
}
if (len > maxLen) {
maxLen = len;
maxBeginIndex = left;
}
}
return s.substring(maxBeginIndex + 1, maxBeginIndex + maxLen + 1);
}
10、盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。不能倾斜容器。
思路:双指针解法——容器的盛水量取决于容器的底和容器较短的那条高。 容器高度较大的一侧的移动只会造成容器盛水量减小,所以应当移动高度较小一侧的指针,并继续遍历,直至两指针相遇 。
假设有左指针 left 和右指针 right ,且 left 指向的值小于 right
的值,如何移动双指针才能保证最大的盛水量被遍历到,假如我们将右指针左移,则右指针左移后的值和左指针指向的值相比有三种情况:
- 右指针指向的值大于左指针这种情况下,容器的高取决于左指针,但是底变短了,所以容器盛水量一定变小;
- 右指针指向的值等于左指针这种情况下,容器的高取决于左指针,但是底变短了,所以容器盛水量一定变小;
- 右指针指向的值小于左指针这种情况下,容器的高取决于右指针,但是右指针小于左指针,且底也变短了,所以容量盛水量一定变小了.
public class MaxAreaTest_4 {
public static void main(String[] args) {
int[] height = new int[]{1, 8, 6, 2, 5, 4, 8, 3, 7};
int maxArea = maxArea(height);
System.out.println(maxArea);
}
private static int maxArea(int[] height) {
int maxArea = 0;
int currentArea = 0;
int left = 0;
int right = height.length - 1;
while (left < right) {
currentArea = Math.min(height[left], height[right]) * (right - left);
maxArea = Math.max(maxArea, currentArea);
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return maxArea;
}
}
11、反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
思路:双指针法
public class ReverseStringTest_5 {
public static void main(String[] args) {
//偶数场景验证
char [] charArray1 = "abcd".toCharArray();
String result = reverseString(charArray1);
System.out.println(result);
//奇数场景验证
char [] charArray2 = "abcde".toCharArray();
result = reverseString(charArray2);
System.out.println(result);
}
/**
* 双指针法
* */
public static String reverseString(char [] charArray) {
if (charArray == null || charArray.length == 0) {
return "";
}
int left = 0;
int right = charArray.length - 1;
while (left < right) {
char temp = charArray[left];
charArray[left] = charArray[right];
charArray[right] = temp;
left++;
right--;
}
return new String(charArray);
}
}