lc-jd

*****************************************************************
一、数

 03. 数组中重复的数字
 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,
 也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

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

//题目中说了有重复的数字
class Solution {
    public int findRepeatNumber(int[] nums) {
        //运用set的排重性解决查找重复的问题
        Set<Integer> set = new HashSet<>();
        //遍历数组,如果放不进去,即是重复的数
        //需要声明全局变量用于保存这个数
        int repeat = -1;
        for(int num:nums){
        	if(!set.add(num)) {
        		repeat = num;
        		break; 
        	}
        }
        return repeat;
    }
}


09. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,
分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

class CQueue {
    Stack<Integer> stk1;
    Stack<Integer> stk2;

    public CQueue() {
        stk1 = new Stack<>();
        stk2 = new Stack<>();
    }
    
    public void appendTail(int value) {
        stk1.push(value);
    }
    
    public int deleteHead() {
        if(!stk2.isEmpty()){
            return stk2.pop();
        }else{
            while(!stk1.isEmpty()) {
                stk2.push(stk1.pop());
            }
            return stk2.isEmpty() ? -1 : stk2.pop();
        }
    }
}


 11. 旋转数组的最小数字
 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
 输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,
 数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1

简单解法:遍历数组,如果后数比前数小,返回后数
正常解法:
class Solution {
    public int minArray(int[] numbers) {
        int l = 0;
        int r = numbers.length - 1;
        while(l < r) {
            int mid = ((r - l) >> 1) + l;
            if(numbers[mid] < numbers[r]) {
                r = mid;
            }else if(numbers[mid] > numbers[r]) {
                l = mid + 1;
            }else {
                r--;
            }
        }
        return numbers[l];
    }
}


 15. 二进制中1的个数
 请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int res = 0;
        while(n != 0) {
            if((n & 1) == 1) {
                res++;
            }
            //注意是无符号数移动
            n >>>= 1;
        }
        return res;
    }
}


17. 打印从1到最大的n位数
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

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

简单解法:
class Solution {
    public int[] printNumbers(int n) {
        int num = (int) Math.pow(10, n);
        //注意num - 1和i -1
        int[] res = new int[num -1];
        for(int i = 1; i < num; i++){
            res[i - 1] = i;
        }
        return res;
    }
}


21. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
class Solution {
    public int[] exchange(int[] nums) {
        int l = 0;
        int r = nums.length - 1;
        while(l < r) {
        	//两头往中间走,遇到位置不对的 对调位置
            while((nums[r] & 1) == 0 && l < r) r--;
            while((nums[l] & 1) == 1 && l < r) l++;
            int tmp = nums[r];
            nums[r] = nums[l];
            nums[l] = tmp;
            //对调完记得继续,直到完全走完l < r
            l++;
            r--;
        }
        return nums;
    }
}



*****************************************************************
二、字符串

05. 替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

简单解法:使用内置函数replace
class Solution {
    public String replaceSpace(String s) {
    	return s.replace(" ", "%20");
    }
}

正规解法
class Solution {
    public String replaceSpace(String s) {
    	int len = s.length();
    	//全都是空格也最多只是3*len长
        char[] array = new char[len * 3];

        //遍历s,定义size对应放进array里
        int size = 0;
        for(int i = 0; i < len; i++) {
            if(s.charAt(i) != ' ') {
                array[size++] = s.charAt(i);
            }else{
                array[size++] = '%';
                array[size++] = '2';
                array[size++] = '0';
            }
        }


        String res = new String(array, 0, size);
        return res;
    }
}






*****************************************************************
三、链表

06. 从尾到头打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:
输入:head = [1,3,2]
输出:[2,3,1]

 方法1、倒序用栈
class Solution {
    public int[] reversePrint(ListNode head) {
    	Stack<Integer> stk = new Stack<>();
    	//把链表的值都压栈
    	ListNode cur = head;
    	while(cur != null) {
    		stk.push(cur.val);
    		cur = cur.next;
    	}
    	//要求数组返回,新建数组存pop
    	//注意,此处必须用变量size 保存stk.size(),否则随着stk.pop,stk.size会变
    	int size = stk.size();
    	int[] res = new int[stk.size()];
    	for(int i = 0; i < stk.size(); i++) {
    		res[i] = stk.pop();
    	}
    	return res;
    }
}



 18. 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。

示例 1:
输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

解法1、单指针
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        if(val == head.val) return head.next;
        ListNode cur = head;
        //看后一节点,如果不是null 并且不是要删除的节点,则往后拱
        while(cur.next != null && cur.next.val != val) {
            cur = cur.next;
        }
        //如上while循环走完,说明cur要么是最后一个节点,要么是要删除的节点

        //分析cur不会走到最后一个节点,因为如果cur是最后一个节点且要删除的就是这最后一个节点,
        //则倒数第二个节点不满足上述while的条件cur.next.val != val

        //所以while走完,要么中间遇到了某个满足条件的节点,
        //要么最后一个节点是要被删除的节点,由倒数第二个节点卡住顺带删除最后一个节点
        //不管哪种情况,如下都可以完成任务
        if(val == cur.next.val) {
            cur.next = cur.next.next;
        }
        return head;
    }
}

解法2、双指针
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        if(head.val == val) return head.next;
        ListNode pre = head, cur = head.next;
        while(cur != null && cur.val != val) {
            pre = cur;
            cur = cur.next;
        }
        if(cur != null) {
        	//只需要处理pre,cur.next传给pre.next——cur这个节点的指向传给别人了
        	//也没人指向cur,所以cur被删了
        	pre.next = cur.next;	
        }
        return head;
    }
}



22. 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.

解法1、双指针
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode fast = head;
        ListNode slow = head;
        for(int i = 0; i < k; i++) {
            fast = fast.next;
        }
        while(fast != null){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}



24. 反转(单)链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

方法1、递归
//递归三要素:1、大问题可以分解成子问题; 2、大问题和子问题解法相同;3、存在最小子问题
class Solution {
    public ListNode reverseList(ListNode head) {
    	//如果没有节点了。或者只剩最后一个节点了就返回,不会再递归了【递3】
        if(head == null || head.next == null) return head;
        //递的过程【递1】
        //新建newHead,就是每次递的那个新头
        ListNode newHead = reverseList(head.next);
        //归【递2】,每次偶读可以认为除了head,剩下的head.next可以看做是一坨已解决过的
        head.next.next = head;
        head.next = null;   
        //返回新头
        return newHead;
    }
}
 

方法2、迭代
class Solution {
    public ListNode reverseList(ListNode head) {
        //如果没有节点了。或者只剩最后一个节点了就返回
        if(null == head || null == head.next) return head;
        //兄弟指针
        ListNode pre = null, cur = head;
        while(cur != null) {
        	//先tmp备份cur.next
            ListNode tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        //经过上一个while,pre已经是尾节点了
        return pre;
    }
}


25. 合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

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

方法1、递归
public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    while( l1 != null && l2 != null) {
        if(l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        }else {
            l2.next = mergeTwoLists(l2.next, l1);
            return l2;
        }
    }
    return l1 == null ? l2 : l1;
}


方法2、迭代
public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    ListNode dummyHead = new ListNode(-1);
    ListNode pre = dummyHead;
    while(l1 != null && l2 != null) {
        if(l1.val <= l2.val) {
            pre.next = l1;
            l1 = l1.next;
        }else {
            pre.next = l2;
            l2 = l2.next;
        }
        //千万要记住这个
        pre = pre.next;
    }
    pre.next = l1 == null ? l2 : l1;
    return dummyHead.next;
}



*****************************************************************
四、二叉树
27. 二叉树的镜像
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
解法1、递归分治
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
    	//递归终止条件【递3】
        if(null == root) return null;
        //分、递【递1】
        TreeNode leftTmp = mirrorTree(root.right);
        TreeNode rightTmp = mirrorTree(root.left);
        //治、归【递2】
        root.left = leftTmp;
        root.right = rightTmp;
        return root;
    }
}

解法2、还是递归
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
    	//递归终止条件
        if(root == null) return null;
        //先把root.left备份起来
        TreeNode tmp = root.left;
        //把root.roght递归操作覆盖root.left
        root.left = mirrorTree(root.right);
        //再备份回来,不起root.right
        root.right = mirrorTree(tmp);
        return root;
    }
}

解法3、辅助栈
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null) return null;
        Stack<TreeNode> stack = new Stack<>() {{ add(root); }};
        while(!stack.isEmpty()) {
            TreeNode node = stack.pop();
            if(node.left != null) stack.add(node.left);
            if(node.right != null) stack.add(node.right);
            TreeNode tmp = node.left;
            node.left = node.right;
            node.right = tmp;
        }
        return root;
    }
}


28. 对称的二叉树
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
class Solution {
    public boolean isSymmetric(TreeNode root) {
        return root == null ? true : recur(root.left, root.right);
    }
    boolean recur(TreeNode L, TreeNode R) {
        if(L == null && R == null) return true;
        if(L == null || R == null || L.val != R.val) return false;
        return recur(L.left, R.right) && recur(L.right, R.left);
    }
}








 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值