希望看起来没那么乱
牛客的发表博客想再次编辑,好像有点小问题,所以只好另起一篇,看看哪里需要更新或者有新的理解。本轮刷题是在leetcode上完成,因此有些名称和牛客oj不太一样。
这里是一刷的目录
03. 数组中重复的数字
参考原来的一刷的博客即可,在一刷的解法4,名字可以叫原地置换。该方法是时间复杂度和空间复杂度最佳,只是会改变原数组。
最优解如下:
Leetcode上是返回该数字,因此稍微有点不同
class Solution {
public int findRepeatNumber(int[] nums) {
int n=nums.length;
for(int i=0;i<nums.length;i++){
//例如5不在索引为5的位置上,就一直和后面的数做交换
//直到出现两个相同,比如2位置上已经是2,而后面又出现了2
//则描述该情况 nums[i]是后面的2 而nums[nums[i]]是前面的2,相同则找到了所求
while(nums[i]!=i){
if(nums[nums[i]] == nums[i]){
return nums[i];
}
int temp = nums[i];
nums[i] = nums[temp];
nums[temp] = temp;
}
}
return -1;
}
}04.二维数组中的查找
参考原来的一刷的博客即可。时间复杂度O(n),利用阶梯型走法。
提供一个Leetcode上通过的写法
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if(matrix == null || matrix.length == 0) {
return false;
}
//以右上角查找为例 则从最后一列开始,从第一行开始
int m = matrix.length;//行
int n= matrix[0].length;//列
int row = 0;
int col = n - 1;
while(row < m && col >= 0) {
if(matrix[row][col] > target) {
col--;
}else if(matrix[row][col] < target) {
row++;
}else {
return true;
}
}
return false;
}
}05.替换空格
依旧参考一刷博客。最容易理解的写法是遇到空格在StringBuider或者StringBuffer上直接添加,而本题考察的是替换,因此,考虑如果给定的是一个字符数组,要求不适用额外的空间,在原数组上进行修改并替换空格,这样的假设下,最正统的方法就是从后往前慢慢加,也就是博客中记录的方法2。考虑本题给定的并不是一个字符数组,因此写法上会有些不同。
同样这里记录一个Leetcode上通过的正统写法。
class Solution {
public String replaceSpace(String s) {
if(s==null || "".equals(s)) return s;
//统计空格数量
int spaceCount = 0;
for(int i = 0 ; i < s.length() ;i++){
if(s.charAt(i) == ' ') {
spaceCount++;
}
}
//由于String是不可变的,为了容纳更多的字符,需要进行扩容
int length1 = s.length();
int length2 = length1+2*spaceCount;
char[] result = new char[length2];
//双指针
int p1 = s.length()-1;
int p2 = result.length-1;
while(p1>=0){
char c = s.charAt(p1);
if(c ==' '){
result[p2--] = '0';
result[p2--] = '2';
result[p2--] = '%';
}else{
result[p2--] = c;
}
p1--;
}
return new String(result);
//return String.valueOf(result);
}
}06. 从尾到头打印链表(有补充)
这里要说明的是,有多种方法,递归,头插,栈,以及二刷要补充的反转链表,其他部分可以参考一刷博客,这里仅记录反转链表的写法。最优解可应该学习栈的写法,但是这个写法不需要额外空间,也是面试中的一个考点
class Solution {
public int[] reversePrint(ListNode head) {
ListNode temp = reverse(head);
LinkedList<Integer> res = new LinkedList<>();
while (temp != null) {
res.add(temp.val);
temp = temp.next;
}
int[] ans = new int[res.size()];
for(int i=0;i<ans.length;i++){
ans[i] = res.get(i);
}
return ans;
}
private ListNode reverse(ListNode node) {
if (node == null || node.next == null ) {
return node;
}
ListNode pre = null;
ListNode cur = node;
while (cur != null) {
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}07. 重建二叉树(有新的认识)
这题比较难,在一刷的写法中,我们每次都需要重新在中序中找到对应的值,但是这里可以用Map代替这一查找动作。思路其实一致,只是在找中序的节点时,一个重新找,一个用容器存了查找结果。如果要求不适用额外空间,那么显然是一刷的写法最好。
map中存放的是中序的某个值,以及该值的角标索引。
public class Solution {
HashMap<Integer, Integer> map = new HashMap<>();
int[] arr;
public TreeNode buildTree(int[] preorder, int[] inorder) {
arr = preorder;
for(int i = 0; i < inorder.length; i++)
map.put(inorder[i], i);
return recur(0, 0, inorder.length - 1);
}
public TreeNode recur(int pre_root, int in_left, int in_right) {
if(in_left > in_right) return null;
TreeNode root = new TreeNode(arr[pre_root]);
int i = map.get(arr[pre_root]);//找到在中序中的位置
//然后计算出各种偏移量。
root.left = recur(pre_root + 1, in_left, i - 1);
root.right = recur(pre_root + i - in_left + 1, i + 1, in_right);
return root;
}
}有必要把原始写法也写出来,方便对比
public class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
TreeNode root=helper(preorder,0,preorder.length-1,inorder,0,inorder.length-1);
//传过去一个两个数组以及这个数组的首尾索引
return root;
}
//前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
public TreeNode helper(int[] pre,int leftP,int rightP,int[] in,int leftI,int rightI)
{
//startPre和endpre以及startIn和endIn的缩写 分别代表传入数组的首尾位置的索引
if(leftP>rightP||leftI>rightI) //递归结束标志(也可加入其他健壮性语句)
return null;
TreeNode root=new TreeNode(pre[leftP]); //找到pre的首节点,就是传过来的树的根
for(int i=leftI;i<=rightI;i++) //遍历传过来的中序,找到根节点。
if(in[i]==pre[leftP]){ //其左边的是子树,调用递归。右边的是右子树,调用递归
root.left=helper(pre,leftP+1,leftP+i-leftI,in,leftI,i-1);
root.right=helper(pre,i-leftI+leftP+1,rightP,in,i+1,rightI);
break;//找到根节点在中序的位置就可以了,可以提前退出。
}
return root;
}
}08.二叉树的下一个节点(Leetcode的剑指offer部分暂无本题但有补充)
移步这里
上面的写法是在递归的过程中比较,显然我们还有一个思路,就是遍历出中序所有结果,然后去比较。但是整体上,一刷记录的写法是最优的,针对于各种情况讨论。因此建议掌握一刷中给出的方法。另外如果是找上一节点的话,我们用栈遍历的时候,可以记录上一个节点,但是下一个节点不好记录。
09.用两个栈实现队列
一刷博客中记录的方法,以下为Leetcode上ac代码
public class CQueue {
Stack<Integer> stack1;
Stack<Integer> stack2;
public CQueue() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
public void appendTail(int value) {
stack1.push(value);//永远往栈1压元素
}
public int deleteHead() {
if(stack1.empty()&&stack2.empty()){
return -1;
}
if(stack2.isEmpty()){ //栈2为空,就需要倒一手,不为空就直接弹,跳到return进行弹
while(!stack1.isEmpty()){
//栈2已经空了想取元素,就得把元素全部倒给栈2,直到栈1是空
stack2.push(stack1.pop()); //把栈1的弹出,全部放入栈2
}
}
return stack2.pop();
}
}


京公网安备 11010502036488号