剑指Offer刷题小结--六(31~36)

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zxzxzx0119/article/details/79956654

目录

  • 第一题:整数中1出现的次数(从1到n整数中1出现的次数)
  • 第二题:把数组排成最小的数
  • 第三题:丑数
  • 第四题:第一个只出现一次的字符
  • 第五题:数组中的逆序对
  • 第六题:两个链表的第一个公共结点

第一题:整数中1出现的次数(从1到n整数中1出现的次数)

题目链接

题目:

在这里插入图片描述

解析:

方法一: 常规解法
    public int NumberOf1Between1AndN_Solution(int n) {
        int res = 0;
        for(int i = 1; i <= n; i++)
            res += numbers(i);
        return res;
    }
    
    private int numbers(int n){
        int sum = 0;
        while(n > 0){
            if(n % 10 == 1)
                sum += 1;
            n /= 10;
        }
        return sum;
    }
方法二: 参照了这位大佬的解题思路

先看个位:
在这里插入图片描述
再看十位:
在这里插入图片描述
总结:
在这里插入图片描述
再举一个栗子: 统1~5246中1的个数:

  • 想到每一位和1的关系,拿5246,先是个位6,个位的变化范围是0~9,而这样的变化,会有524次,所以这里有524个1,又因为最后一次有个6,所以还要加一次,所以个位的1的个数是524+1 = 525;
  • 再看十位,十位上的数字是4,所以同理,这个位数的上的1的个数应该是52 * 10,注意这里不是52 * 1,因为,10位上的数后面10-20之间有10个1,且最后4>1,所以还要加上10,所以十位上的1的个数是52 * 10+10 = 530。这里要注意如果十位上的数字是1的话,就要看个位上的数是多少了,也就是10 ~ 20之间取多少个,这时候我们只要计算n%10+1就行了
  • 然后同理推高位,可以得到1~5246中1的个数是(524 * 1+1)+(52 * 10+10)+(5 * 100+100) +( 0 * 1000+1000) = 2655个。
public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        if(n <= 0)
            return 0;
        int res = 0;
        int base = 1,cur,height = n;  // base表示当前判断的位数、cur表示当前位、height表示高位
        while(height > 0){
            cur = height % 10;
            height /= 10; 
            res += height * base; //先加上一开始的
            if(cur == 1){
                res += (n%base)+1; //==1 就要看前面的了
            }else if(cur > 1){
                res += base; //后面剩的,>1 还要+base
            }
            base *= 10;
        }
        return res;
    }
}

其他方法:

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        if (n <= 0) 
            return 0;
        int len = getLenOfNum(n);
        if (len == 1) 
            return 1;
        int tmp1 = (int) Math.pow(10,len - 1);
        int first = n / tmp1;
        int firstOneNum = first == 1 ? n % tmp1 + 1 : tmp1;
        int otherOneNum = first * (len - 1) * (tmp1 / 10);
        return firstOneNum + otherOneNum + NumberOf1Between1AndN_Solution(n % tmp1);
    }
    public int getLenOfNum(int num) {
        int len = 0;
        while (num != 0) {
            len++;
            num /= 10;
        }
        return len;
    }
}
public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        return countDigit(n,1);
    }
    public int countDigit(int n, int digit) { // 0 < digit <= 9
        if (n < digit) 
            return 0;
        else if (n < 10) 
            return 1;
        int scale = 1;
        for (int t = n / 10; t > 0; t /= 10)
            scale *= 10;
        int highestDigit = n / scale;
        int res = 0; 
        res += highestDigit * countDigit(scale - 1, digit);// ex) countDigit(2345, 2) -> 2 * countDigit(999, 2)
        res += countDigit(n % scale, digit);               // ex) countDigit(2345, 2) -> countDigit(345, 2) 
        if (highestDigit > digit)                          // ex) countDigit(2345, 1) --> 1000
            res += scale; 
        else if (highestDigit == digit)                    //ex) countDigit(2345, 2) --> 345 + 1
            res += n % scale + 1; 
        return res;
    }
}

dp:

import java.util.LinkedList;
public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        if(n <= 0)
            return 0;
        int[][] f = new int[30][2];
        int[][] g = new int[30][2];
        LinkedList<Integer>digits = new LinkedList<>();
        for(; n > 0; n /= 10)
            digits.addFirst(n % 10);
        f[0][1] = 0;
        g[0][1] = 1;
        for(int i = 0; i < digits.size(); i++) {
            for (int j = 0; j < 10; j++) {
                g[i+1][0] += g[i][0];
                f[i+1][0] += f[i][0] + (g[i][0] * (j == 1 ? 1 : 0 ));
                if(j < digits.get(i)){
                    g[i+1][0] += g[i][1];
                    f[i+1][0] += f[i][1] + (g[i][1] * (j == 1 ? 1 : 0 ));
                }else if(j == digits.get(i)){
                    g[i+1][1] += g[i][1];
                    f[i+1][1] += f[i][1] + (g[i][1] * (j == 1 ? 1 : 0 ));
                }
            }
        }
        return f[digits.size()][0] + f[digits.size()][1];
    }
}

第二题:把数组排成最小的数

题目链接

题目:

在这里插入图片描述

解析

这个题目要想到字符串,想到一个排序规则,两个数a,b,把它们转换成字符串,把它们连起来,如果a+b > b+a(注意我这里+的意思是代表连起来),则我们要选择b+a,按照这个规则来排序,按照小的排在前面就行了。

public String PrintMinNumber(int[] numbers) {
		ArrayList<String>list = new ArrayList<String>();
		for(int i = 0; i < numbers.length; i++)list.add(numbers[i]+"");
		Collections.sort(list, new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				return (o1+o2).compareTo(o2+o1);  //按照降序排列(第一个大于第二个返回1-->升序排列)
			}
		});
		String str = "";
		for(String temp:list)str += temp;
		return str;
	}	

第三题:丑数

题目链接

题目:

在这里插入图片描述

解析:

这个题目按照直接一个个判断是不是丑数的方法不是好方法,要想到的是可以从小到大计算出丑数,存到一个数组中,这里要注意的是丑数的顺序要是从小到大的排列(因为等下要按照下标取),所以每次生成的时候,要选择最小的丑数生成,设置三个小标i2,i3,i5记录三个因子各自的下标,每次比较生成即可。

 public int GetUglyNumber_Solution(int index) {
    	if(index == 0)return 0;
    	int[] ans = new int[index+1];
    	int count = 0,i2 = 0,i3 = 0,i5 = 0;
    	ans[0] = 1;
    	while(count < index) {
    		int temp = Math.min(ans[i2]*2, Math.min(ans[i3]*3, ans[i5]*5));
    		if(temp == ans[i2] * 2)i2++;
    		if(temp == ans[i3] * 3)i3++;
    		if(temp == ans[i5] * 5)i5++;
    		ans[++count] = temp;
    	}
    	return ans[index-1];
    }

第四题:第一个只出现一次的字符

题目链接

题目:

在这里插入图片描述

解析:

直接用哈希表,一个计数数组记录每个字母出现的次数,两次O(n)的遍历,第二次从前往后判断,只要字母只出现一次就直接返回下标即可。

public int FirstNotRepeatingChar(String str) {
		if(str == null || str.length() == 0)return -1;
		int[] a = new int[59]; //65~122
		for(int i = 0; i < str.length(); i++)a[str.charAt(i)-'A'+1]++;
		for(int i = 0; i < str.length(); i++)if(a[str.charAt(i)-'A'+1] == 1)return i;
        return -1;
    }
	
	//熟悉一下HashMap的写法
	public int FirstNotRepeatingChar2(String str) {
		if(str == null || str.length() == 0)return -1;
		HashMap<Character,Integer> mp = new HashMap<Character,Integer>();
		for(int i = 0; i < str.length(); i++) {
			if(!mp.containsKey(str.charAt(i))) {
				mp.put(str.charAt(i), 1);
			}else {
				mp.put(str.charAt(i), mp.get(str.charAt(i))+1);
			}
		}
		for(int i = 0; i < str.length(); i++)if(mp.get(str.charAt(i)) == 1)return i;
		return -1;
	}

第五题:数组中的逆序对

题目链接

题目:

在这里插入图片描述

解析:

这个题目用到归并排序,分开到底之后,每次合并,判断一下两个数的大小,如果左边的data[i] > data[j],那么逆序数,就可以加上j - mid(前面的(mid ~ j)都会比data[i] 要小),并且把data[i]加到辅助数组的后面,一步一步归并即可。

public int InversePairs(int[] data) {
		if (data.length == 0 || data == null)return 0;
		int[] temp = new int[data.length];
		int sum = Merge(temp, data, 0, data.length - 1);
		return sum;
	}

	public static int Merge(int[] temp, int[] data, int l, int r) {
		if (l == r) {// 递归条件
			temp[l] = data[l];
			return 0;
		}
		int mid = l + (r - l) / 2;
		int leftcount = Merge(temp, data, l, mid);
		int rightcount = Merge(temp, data, mid + 1, r);
		int i = mid, j = r, k = r,count = 0;
		while (i >= l && j > mid) {
			if (data[i] > data[j]) {
				count += j - mid;
				if(count >= 1000000007)count %= 1000000007; //这里很大的时候要去余
				temp[k--] = data[i--];
			} else {
				temp[k--] = data[j--];
			}
		}
		while (i >= l)temp[k--] = data[i--];
		while (j > mid)temp[k--] = data[j--];
		for(int p = l; p <= r; p++)data[p] = temp[p];  //把已经排序的数组考到原数组中
		return (count + leftcount + rightcount)%1000000007;
	}

第六题:两个链表的第一个公共结点

题目链接

题目:

在这里插入图片描述

解析:

关键就是要理解结点,而不是结点的值,一旦有一个结点相同,后面的都会相同
这里写图片描述

	public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
		if (pHead1 == null || pHead2 == null)return null;
		int len1 = getListLength(pHead1),len2 = getListLength(pHead2);
		int lenDif = len1 - len2;
		ListNode pLong = pHead1,pShort = pHead2;
		if(len2 > len1) {
			pLong = pHead2;
			pShort = pHead1;
			lenDif = len2 - len1;
		}
		for(int i = 0; i < lenDif; i++)pLong = pLong.next;
		for(;pLong != pShort && pLong != null && pShort != null; pLong = pLong.next,pShort = pShort.next);
		return pLong;
	}
	private static int getListLength(ListNode node) {
		int len = 0;
		ListNode p = node;
		while(p != null) {
			p = p.next;
			len++;
		}
		return len;
	}
阅读更多

没有更多推荐了,返回首页