题目一:
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
方法一:
1、这是一个数组的就地删除操作,通过双指针可以将其实现;
2、设置一个快指针,一个慢指针,循环遍历一个变量是实现不了的;
3、慢指针i=0;快指针遍历数组,如果是删除的值,直接跳过,否则将元素复制到慢指针数组中,慢指针加1;
4、这里最有趣的是如果数组元素和比较元素不相等,直接跳过;
具体代码:
class Solution { public int removeElement(int[] nums, int val) { int j=0; for(int i=0;i<nums.length;i++) { if(nums[i]!=val) { nums[j]=nums[i]; j++; } } return j; } }
方法二:
1、如果数组的值和value值相同,将最后一个元素赋给这个值;
2、n--将数组最后一个值删除;
具体代码:
class Solution { public int removeElement(int[] nums, int val) { int i=0; int n=nums.length; while(i<n) { if(nums[i]==val) { nums[i]=nums[n-1]; n--; } else { i++; } } return n; } }
题目二:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
方法一:
1、主要使用哈希表,将值进行存储起来;
2、for(i:nums) Integer count=map.get(i);count=count==1?1:++count;
3、进行搜索,for(i:map.keySet())
具体代码:
class Solution { public int singleNumber(int[] nums) { Map<Integer, Integer> map=new HashMap<>(); for(int i:nums) { Integer count=map.get(i); count=(count==null)?1:++count; map.put(i, count); } for(int i:map.keySet()) { Integer count=map.get(i); if(count==1) return i; } return -1; } }
方法二:
1、异或操作,主要是异或操作的两个性质;
2、两个相同的数进行以后操作结果为0;0和一个数进行异或操作结果依旧为这个数;
3、以后操作有点强;
具体代码:
class Solution { public int singleNumber(int[] nums) { int result=0; for(int i=0;i<nums.length;i++) { result=result^nums[i]; } return result; } }
题目三:
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
方法一:
1、暴力法,也就是通过递归实现;
2、总方法=先上一个楼梯到n个楼梯+先上2个楼梯到n个楼梯;
3、递归出口:如果i==n,return 1; 如果i>n return 0;
具体代码:
class Solution { public int climbStairs(int n) { return climb_stairs(0, n); } public int climb_stairs(int i,int n) { if(i>n) return 0; if(i==n) return 1; return climb_stairs(i+1, n)+climb_stairs(i+2, n); } }
方法二:
1、问题的最优解可以通过子问题的最优解得出,使用动态规划;
2、状态方程:到第i楼梯的解法=到第i-1楼梯解法+到第i-2楼梯解法-------dp[i]=dp[i-1]+dp[i-2];
3、返回dp[n];
具体代码:
public class Solution { public int climbStairs(int n) { if(n==1) return 1; int[] dp=new int[n+1]; dp[1]=1; dp[2]=2; for(int i=3;i<=n;i++) { dp[i]=dp[i-1]+dp[i-2]; } return dp[n]; } }
方法三:
1、使用斐波那契数列,有趣的是dp[i]=dp[i-1]+dp[i-2]刚好是斐波那契数列;
2、可以省去空间对一些值的存储;
具体代码:
public class Solution { public int climbStairs(int n) { if(n==1) return 1; int first=1; int second=2; for(int i=3;i<=n;i++) { int third=first+second; first=second; second=third; } return second; } }
题目四:
给定两个二进制字符串,返回他们的和(用二进制表示)。
输入为非空字符串且只包含数字 1 和 0。
示例 1:
输入: a = "11", b = "1"
输出: "100"
方法:
1、二进制求和,其实和10进制求和类似,先从最后一位开始,有2进1;
2、因为返回的是字符串,而中间需要获取单个字符,返回的数据类型为StringBuilder;
3、具体的计算过程:定义ca=0;sum+=i>=0?a.charAt(i)-'0':0; 对字符串类似进行相加;
4、得到最后一位:ans.append(sum%2);
5、获取进位ca=sum/2;
6、最后判断是否有进位ca,ans.append(ca==0? 1:" ");
7、将字符串进行反转转化为String类型;
最后说明使用StringBuilder和StringBuffer的区别,其实这两个类功能基本相同,只不过StringBuffer是线程安全,StringBuilder线程不安全但单线程相对较快;
具体代码:
class Solution { public String addBinary(String a, String b) { StringBuilder anStringBuilder=new StringBuilder(); int ca=0; for(int i=a.length()-1,j=b.length()-1;i>=0||j>=0;i--,j--) { int sum=ca; sum+=i>=0?a.charAt(i)-'0':0; sum+=j>=0?b.charAt(j)-'0':0; anStringBuilder.append(sum%2); ca=sum/2; } anStringBuilder.append(ca==1? ca:""); return anStringBuilder.reverse().toString(); } }
题目五:
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
方法一:
1、通过迭代方式,逐步实现;
2、核心思想是设置当前指针curr,需要一个临时节点保存当前节点指向的下一个节点,将curr.next指向前一个元素;
3、再将curr节点赋值到pre节点,临时节点temp赋值到curr节点,继续向后遍历;
4、返回pre节点就是解;
具体代码;
class Solution { public ListNode reverseList(ListNode head) { ListNode pre=null; ListNode curr=head; while(curr!=null) { ListNode temp=curr.next; curr.next=pre; pre=curr; curr=temp; } return pre; } }
方法二:
1、通过递归实现,递归出口:if(head==null||head.next==null) return head
2、求reserveList(head.next);
3、head.next.next=head;head.next=null;思考递归一定要整体考虑,可以求得不包括head的其他节点的反链表;
4、再通过指针的指向实现和头节点的链接;
具体代码:
class Solution { public ListNode reverseList(ListNode head) { if(head==null||head.next==null) return head; ListNode pListNode=reverseList(head.next); head.next.next=head; head=null; return pListNode; } }
题目六:
给定一个仅包含大小写字母和空格 ' ' 的字符串,返回其最后一个单词的长度。
如果不存在最后一个单词,请返回 0 。
说明:一个单词是指由字母组成,但不包含任何空格的字符串。
示例:
输入: "Hello World"
输出: 5
方法:
1、逆向思维,从字符串的最后开始遍历,遇到空格停止遍历,将其距离输出;
2、如果刚开始最后的字符为空格,应该先把这些空格去掉,end=s.lenght();while(end>=0&&s,charAt(end)==' ') end--;
3、接着取最后一位到第一个空格后的距离:int start=end,while(start>=0&&s.charAt(start)!=' ') start--;
4、返回end-start;
具体代码:
class Solution { public int lengthOfLastWord(String s) { int end=s.length()-1; while(end>=0&&s.charAt(end)==' ') end--; int start=end; while(start>=0&&s.charAt(start)!=' ') start--; return end-start; } }
题目七:
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
方法一:
1、递归法;
2、求出根节点左节点的最大深度和右节点的最大深度进行比较求最大值,然后再加1;
具体代码:
public int maxDepth(TreeNode root) { int result=0; if(root==null) return 0; else { int left_height=maxDepth(root.left); int right_height=maxDepth(root.right); result=Math.max(left_height, right_height)+1; } return result; }
题目八:
public class Main{ public static int main(String[] args) { Scanner input=new Scanner(System.in); int n=input.nextInt(); int[] w=new int[n]; for(int i=0;i<n;i++) { w[i]=input.nextInt(); } int m=input.nextInt(); int[] h=new int[m]; for(int i=0;i<m;i++) { h[i]=input.nextInt(); } Arrays.sort(w); Arrays.sort(h); int count=0; int stu=0; for(int i;i<n;i++) { if(w[i]>h[stu]) continue; else { count++; stu++; if(stu==n) break; } } System.out.println(count); } }
题目九:
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
示例 1:
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]
方法:
使用双指针和临时变量进行,再用一个while循环。
具体代码:
class Solution { public void reverseString(char[] s) { int i=0,j=s.length-1; while(i<j) { char temp=s[j]; s[j]=s[i]; s[i]=temp; i++; j--; } } }