39.数组中出现次数超过一半的数字
思路:count==0,时,val=num[i],count=1;相同count++,不同count--;最后判断是否存在超过一半的数字(传递参数【超过一半的那个数或者最后一个数】)
时间复杂度o(n),空间复杂度O(1)
class Solution {
public int moreThanHalfNum_Solution(int[] nums) {
int count=0,val=-1;
for(int i=0; i<nums.length; i++){
if(count==0){
val=nums[i];
count=1;
}
else{
if(nums[i]==val) count++;
else count--;
}
}
if(count>0){
if(!moreThanHalf(nums, val)){//判断是否存在超过一半的数字
return 0;
}
else
return val;
}
return val;
}
public boolean moreThanHalf(int[] nums, int result){
int count=0;
for(int i=0; i<nums.length; i++){
if(nums[i]==result) count++;
}
if(count>(nums.length>>1)) return true;
else return false;
}
}
40. 最小的k个数
思路:维护容量为k的最大堆。
class Solution {
public List<Integer> getLeastNumbers_Solution(int [] input, int k) {
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>((o1, o2) -> o2 - o1);//大根堆
if(k>input.length || input.length==0) return new ArrayList<>();
for(int i=0;i<input.length;i++){
maxHeap.add(input[i]);
if(maxHeap.size()>k){
maxHeap.poll();
}
}
List<Integer> ans = new ArrayList<Integer>();
while(!maxHeap.isEmpty()){
ans.add(maxHeap.poll());//
}
Collections.reverse(ans);//翻转链表
return ans;
}
}
41.数据流的中位数
思路:用最大堆和最小堆维护中位数左边和中位数右边。
class Solution {
public PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1,o2)->o2-o1);
public PriorityQueue<Integer> minHeap = new PriorityQueue<>();
int index = 0;
public void insert(Integer num) {
if((index&1)==0){//第偶数个数,加入右边(最小堆)
maxHeap.add(num);
minHeap.add(maxHeap.poll());
}
else{
minHeap.add(num);//第奇数个数,加入左边(最大堆)
maxHeap.add(minHeap.poll());
}
index++;
}
public Double getMedian() {
if(maxHeap.size()-minHeap.size()==1){
return (double)maxHeap.peek();
}
else if(maxHeap.size()-minHeap.size()==-1){
return (double)minHeap.peek();
}
else{
return ((maxHeap.peek() + minHeap.peek()) / 2.0);
}
}
}
42.连续子数组的最大和
思路:dp[i] = dp[i - 1] >= 0 ? dp[i - 1] + nums[i] : nums[i]
class Solution {
public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
int ans = nums[0];
for(int i=1; i<nums.length; i++){
if(dp[i-1]<0) dp[i] = nums[i];
else dp[i] = nums[i]+dp[i-1];
ans = Math.max(ans, dp[i]);
}
return ans;
}
}
43. 1-n整数中1出现的次数
思路:(1)遍历,查每个数中1的个数,累加。时间复杂度O(nlogn)。(2)从高位到低位查找该位为1时,可选择数字的个数。分为left和right。
class Solution {
public int numberOf1Between1AndN_Solution(int n) {
List<Integer> nums = new ArrayList<>();
while(n!=0){//把每一位放到List里面
nums.add(n%10);
n = n/10;
}
int ans = 0;
for(int i=nums.size()-1; i>=0; i--){
int left=0, right=0, t=1;
for(int j=nums.size()-1; j>i; j--) left = left*10 + nums.get(j);//左边的数字
for(int j=i-1; j>=0; j--){//右边的数字
right = right*10+nums.get(j);
t=t*10;
}
ans = ans + left*t;//答案加上左边的数组*t
if(nums.get(i)==1) ans = ans + right+1;//nums[i]==1
else if(nums.get(i)>1) ans = ans + t;//nums[i]!=1
}
return ans;
}
}
44.数字序列中某一位的数字
思路:先找到多少位,在找到这个数字,最后找到它在这个数字中的位数。
class Solution {
public int digitAtIndex(int n) {
if(n==0) return 0;
int i=1,s=9,base=1;//i位数有s个,开始的数字是base
while(n>i*s){
n -= i*s;
i++;
s*=10;
base*=10;
}
int number = base + (n+i-1)/i-1;//n/i上取整。number为该位所属的数字
int r;//该位属于数字的第r位
if(n%i==0) r=i;//余数为0 return i;
else r=n%i;//不为0 return n%i
for(int k=0; k<i-r;k++) number/=10;
return number%10;
}
}
45.把数组排成最小的数
思路:重载排序。证明排序是有效的(1)反对称性(2)传递性
class Solution {
public String printMinNumber(int[] nums) {
if(nums==null) return null;
String[] numString = new String[nums.length];//先变成字符串数组
for(int i=0; i<nums.length; i++){
numString[i] = nums[i]+"";
}
Arrays.sort(numString, (s1, s2) -> (s1 + s2).compareTo(s2 + s1));//自定义排序array可以这样Collection不可以
String ans = "";
for (String str : numString)
ans += str;
return ans;
}
}
46.把数字翻译成字符串
思路:(1)状态表示(2)状态计算(3)边界定义
class Solution {
public int getTranslationCount(String s) {
if(s=="") return 1;
int[] f = new int[s.length()];//dp问题
f[0] = 1;//
for(int i=1; i<s.length(); i++){
f[i]=f[i-1];//一位
if(i>1){//解决越界问题
//两位
if((s.charAt(i-2)-'0')*10+(s.charAt(i-1)-'0')>=10 && (s.charAt(i-2)-'0')*10+(s.charAt(i-1)-'0')<=25) f[i] += f[i-2];
}
}
return f[s.length()-1];
}
}
60. 礼物的最大价值
思路:(动态规划) O(n2)
dp[i][j] 代表走到(i,j)这个位置的最大价值
由于只能向下向右走,所以(i,j)这个位置只能从左边和上边过来,所以dp[i][j] = max(dp[i-1][j],dp[i][j-1]) + grid[i][j],即从左边和上边这两个位置选一个较大的加上本位置的价值,即可。
class Solution {
public int getMaxValue(int[][] grid) {
int m = grid.length, n = grid[0].length;
int[][] fn = new int[m+1][n+1];
for(int i=1; i<m+1; i++){
for(int j=1; j<n+1; j++){
fn[i][j] = Math.max(fn[i-1][j], fn[i][j-1]) + grid[i-1][j-1];//注意fn偏移了一位
}
}
return fn[m][n];
}
}
48.最长不含重复字符的子字符串
思路:
49.丑数
思路:每次计算的结果中,有一部分是小于等于U,另一部分是大于U,那么大于U里面最小的M就是下一个丑数了。选定三个数U2/U3/U5,那么下一个U就=min(2U2,3U3,5*U5)。我们定义U2乘以2后是下一个可能的丑数,U3乘以3后是下一个可能的丑数,U5乘以5后是下一个可能的丑数。所以每次求完丑数,都要将这三个“候选人”更新。
class Solution {
public int getUglyNumber(int n) {
if(n<=0) return 0;
int[] ans = new int[n];
int i2=0, i3=0, i5=0;
ans[0]=1;//第一个丑数为1
for(int i=1;i<n;i++){
int min = Math.min(ans[i2]*2,Math.min(ans[i3]*3, ans[i5]*5));//找到下一个最小的丑数
ans[i] = min;//加入数组
while(ans[i2]*2<=min) i2++;//i2指向下一个可能的下一个丑数(*2比当前丑数大)
while(ans[i3]*3<=min) i3++;//...
while(ans[i5]*5<=min) i5++;//...
}
return ans[n-1];
}
}
50.第一个只出现一次的字符
50-1 字符串中第一个只出现一次的字符
思路:哈希表,时间复杂度O(n),空间复杂度O(1)。
class Solution {
public char firstNotRepeatingChar(String s) {
int[] count = new int[256];
for(int i=0; i<s.length(); i++){
count[s.charAt(i)]++;
}
for(int i=0; i<s.length(); i++){
if(count[s.charAt(i)]==1)
return s.charAt(i);
}
return '#';
}
}
50-2 字符流中第一个只出现一次的字符
思路:哈希表,时间复杂度O(n),空间复杂度O(n)。哈希表存储出现过的字符,队列里存储只出现过1次的字符。
class Solution {
Map<Character, Integer> charCount = new HashMap<>();//存储出现过的字符
Queue<Character> char1 = new LinkedList<>();//存储只出现过一次的字符
//Insert one char from stringstream
public void insert(char ch){
if(charCount.containsKey(ch)){
if(char1.contains(ch)) {
char1.remove(ch);
}
return;
}
charCount.put(ch, 1);
char1.offer(ch);
}
//return the first appearence once char in current stringstream
public char firstAppearingOnce(){
if(char1.isEmpty()==true) return '#';
return char1.peek();
}
}
51. 数组中的逆序对
思路:(1)暴力,时间复杂度O(n²);(2)归并排序,时间复杂度O(nlogn),需要辅助空间O(n)。
class Solution {
int count=0;
public int inversePairs(int[] nums) {
int l=0, r=nums.length-1, m=(l+r)>>1;
int[] tmp = new int[nums.length];
MergeSort(nums, tmp, l, r);
return count;
}
public void MergeSort(int[] nums, int[] tmp, int l, int r){
if(l>=r) return;
int m = (l+r)>>1;
MergeSort(nums, tmp, l, m);
MergeSort(nums, tmp, m+1, r);
int i=l, j=m+1, k=0;
while(i<=m && j<=r){
if(nums[i]>nums[j]){
count = count + m-i+1;
tmp[k++] = nums[j++];
}
else tmp[k++] = nums[i++];
}
while(i<=m) tmp[k++] = nums[i++];
while(j<=r) tmp[k++] = nums[j++];
k=0;
for(i=l;i<=r;i++){
nums[i] = tmp[k];
k++;
}
}
}
52. 两个链表的第一个公共节点
思路:设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。
当访问链表 A 的指针访问到链表尾部时,令它从链表 B 的头部重新开始访问链表 B;同样地,当访问链表 B 的指针访问到链表尾部时,令它从链表 A 的头部重新开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。
class Solution {
public ListNode findFirstCommonNode(ListNode headA, ListNode headB) {
if(headA==null || headB==null) return null;
ListNode p=headA, q=headB;
while(p!=q){
if(p!=null) p = p.next;
else p=headB;
if(q!=null) q=q.next;
else q=headA;
}
return p;
}
}