JZ43 1~n整数中1出现的次数
按位递推计算,如果当前位的值为0,当前位1的出现次数只由高位决定(高位 * 当前位数);如果当前位的值为1,当前位1的出现次数由高位和低位决定(高位 * 当前位数+低位+1);如果当前位为2~9,当前位1的出现次数由高位决定((高位+1) * 当前位数)
class Solution {
public int countDigitOne ( int n) {
int cur = n % 10 ;
int low = 0 ;
int high = n / 10 ;
int digit = 1 ;
int res = 0 ;
while ( high != 0 || cur != 0 ) {
if ( cur == 0 ) {
res += high * digit;
} else if ( cur == 1 ) {
res += high * digit + low + 1 ;
} else {
res += ( high + 1 ) * digit;
}
low += cur * digit;
cur = high % 10 ;
high /= 10 ;
digit *= 10 ;
}
return res;
}
}
JZ44 数字序列中某一位的数字
先判断在哪个占位范围,再判断在范围内第几个数的第几位(可转为字符串计算),注意部分数值需要使用long定义防止溢出
时间复杂度O(logn),空间复杂度O(logn)
class Solution {
public int findNthDigit ( int n) {
if ( n == 0 ) return 0 ;
int i = 1 ;
long start = 1 ;
long num = 9 ;
long sum = 9 ;
while ( n >= sum) {
n -= sum;
i++ ;
start *= 10 ;
num = 9 * start;
sum = num * i;
}
int a = n / i;
int b = n % i;
if ( b == 0 ) return getNum ( start + a - 1 , b) ;
return getNum ( start + a, b) ;
}
private int getNum ( long num, int digit) {
String num_str = String . valueOf ( num) ;
if ( digit == 0 ) return num_str. charAt ( num_str. length ( ) - 1 ) - '0' ;
return num_str. charAt ( digit - 1 ) - '0' ;
}
}
JZ45 把数组排成最小的数
使用快排,将遍历数组,排序规则为将两数拼接后的较小值作为顺序(使用字符串compareTo()逐个比较ASCII码大小)
class Solution {
public String minNumber ( int [ ] nums) {
int leng = nums. length;
String [ ] numsStr = new String [ leng] ;
for ( int i = 0 ; i < leng; i++ ) {
numsStr[ i] = String . valueOf ( nums[ i] ) ;
}
quickSort ( numsStr, 0 , leng - 1 ) ;
StringBuilder sb = new StringBuilder ( "" ) ;
for ( String s : numsStr) {
sb. append ( s) ;
}
return sb. toString ( ) ;
}
private void quickSort ( String [ ] numsStr, int left, int right) {
if ( right - left < 1 ) return ;
int i = left;
int j = right;
while ( i < j) {
while ( i < j && ( numsStr[ left] + numsStr[ j] ) . compareTo ( numsStr[ j] + numsStr[ left] ) <= 0 ) j-- ;
while ( i < j && ( numsStr[ left] + numsStr[ i] ) . compareTo ( numsStr[ i] + numsStr[ left] ) >= 0 ) i++ ;
swap ( numsStr, i, j) ;
}
swap ( numsStr, i, left) ;
quickSort ( numsStr, left, i - 1 ) ;
quickSort ( numsStr, i + 1 , right) ;
}
private void swap ( String [ ] numsStr, int i, int j) {
String temp = numsStr[ i] ;
numsStr[ i] = numsStr[ j] ;
numsStr[ j] = temp;
}
}
JZ46 把数字翻译成字符串
动态规划,一个或两个数字组成一个字母(青蛙爬楼梯),判断两个数字是否符合条件
时间复杂度O(logn),空间复杂度O(logn)
class Solution {
public int translateNum ( int num) {
if ( num < 10 ) return 1 ;
String numStr = String . valueOf ( num) ;
int a = 1 ;
int b = 1 ;
if ( numStr. substring ( 0 , 2 ) . compareTo ( "25" ) <= 0 ) {
b = 2 ;
}
int c = b;
for ( int i = 2 ; i < numStr. length ( ) ; i++ ) {
if ( numStr. substring ( i - 1 , i + 1 ) . compareTo ( "25" ) <= 0 && numStr. substring ( i - 1 , i + 1 ) . compareTo ( "10" ) >= 0 ) {
c = a + b;
} else {
c = b;
}
a = b;
b = c;
}
return c;
}
}
JZ47 礼物的最大价值
动态规划,当前位置得到的最大价值为左边和右边对比(不同路径),使用滚动数组
时间复杂度O(m*n),空间复杂度O(n)(在原地修改只需O(1))
class Solution {
public int maxValue ( int [ ] [ ] grid) {
int m = grid. length;
int n = grid[ 0 ] . length;
int [ ] dp = new int [ n] ;
dp[ 0 ] = grid[ 0 ] [ 0 ] ;
for ( int j = 1 ; j < n; j++ ) {
dp[ j] = dp[ j - 1 ] + grid[ 0 ] [ j] ;
}
for ( int i = 1 ; i < m; i++ ) {
dp[ 0 ] += grid[ i] [ 0 ] ;
for ( int j = 1 ; j < n; j++ ) {
dp[ j] = Math . max ( dp[ j - 1 ] , dp[ j] ) + grid[ i] [ j] ;
}
}
return dp[ n - 1 ] ;
}
}
JZ48 最长不含重复字符的子字符串
class Solution {
public int lengthOfLongestSubstring ( String s) {
if ( s. length ( ) == 0 ) return 0 ;
int dp = 1 ;
int max = 1 ;
for ( int i = 1 ; i < s. length ( ) ; i++ ) {
int j = i - 1 ;
while ( j >= 0 ) {
if ( s. charAt ( i) == s. charAt ( j) ) {
break ;
} else {
j-- ;
}
}
if ( dp >= i - j) {
dp = i - j;
} else {
dp += 1 ;
}
max = Math . max ( dp, max) ;
}
return max;
}
}
双指针(滑动窗口)+哈希表,子串问题(连续),右指针向前遍历字符串,哈希表统计右指针指向字符最后一次出现的位置;当哈希表更新时(出现重复字符时),左指针移动到哈希表更新前的位置,得到此时右指针指向的下标之前的最长子串(右指针-左指针),同时更新最大值;右指针继续向前移动
class Solution {
public int lengthOfLongestSubstring ( String s) {
if ( s. length ( ) == 0 ) return 0 ;
int left = - 1 ;
int right = 0 ;
int max = 1 ;
Map < Character , Integer > map = new HashMap < > ( ) ;
while ( right < s. length ( ) ) {
if ( map. containsKey ( s. charAt ( right) ) ) {
left = Math . max ( left, map. get ( s. charAt ( right) ) ) ;
}
map. put ( s. charAt ( right) , right) ;
max = Math . max ( max, right - left) ;
right++ ;
}
return max;
}
}
JZ49 丑数
动态规划,每个丑数都是由较小的丑数乘2,3,5得到,使用三指针,指针指向的丑数分别乘2,3,5,取其中最小值对应指针向前移动。
class Solution {
public int nthUglyNumber ( int n) {
int [ ] dp = new int [ n + 1 ] ;
dp[ 1 ] = 1 ;
int a = 1 ;
int b = 1 ;
int c = 1 ;
for ( int i = 2 ; i <= n; i++ ) {
dp[ i] = Math . min ( Math . min ( dp[ a] * 2 , dp[ b] * 3 ) , dp[ c] * 5 ) ;
if ( dp[ i] == dp[ a] * 2 ) a++ ;
if ( dp[ i] == dp[ b] * 3 ) b++ ;
if ( dp[ i] == dp[ c] * 5 ) c++ ;
}
return dp[ n] ;
}
}
JZ50 第一个只出现一次的字符
两次遍历,第一次使用哈希表存储字符及其频率(可使用布尔值表示频率是否符合条件,超过1则为false),第二次返回第一个频率为1的字符。可使用有序哈希表(LinkedHashMap)则第二次不需要遍历字符串只需要遍历有序哈希表。
时间复杂度O(n),空间复杂度O(|字符集大小(最大为26)|)
class Solution {
public char firstUniqChar ( String s) {
Map < Character , Integer > map = new HashMap < > ( ) ;
char [ ] sArr = s. toCharArray ( ) ;
for ( char c : sArr) {
if ( ! map. containsKey ( c) ) map. put ( c, 0 ) ;
map. put ( c, map. get ( c) + 1 ) ;
}
for ( char c : sArr) {
if ( map. get ( c) == 1 ) return c;
}
return ' ' ;
}
}
JZ51 数组中的逆序对*
归并排序——>逆序对,分而治之,归并算法划分后进行合并时对左右数组进行判断,定义左右指针 i ,j 分别指向左右数组起始位置。当 j 向前移动时(右边值较小),逆序对数量为(左边数组长度 - i);直到左右数组遍历完毕,进入下一轮合并判断
class Solution {
int count;
public int reversePairs ( int [ ] nums) {
if ( nums. length <= 1 ) return 0 ;
int [ ] nums2 = new int [ nums. length] ;
int [ ] temp = new int [ nums. length] ;
count = 0 ;
for ( int i = 0 ; i < nums. length; i++ ) {
nums2[ i] = nums[ i] ;
}
mergeSort ( nums2, 0 , nums. length - 1 , temp) ;
return count;
}
private void mergeSort ( int [ ] nums, int left, int right, int [ ] temp) {
if ( right - left < 1 ) return ;
int mid = ( left + right) / 2 ;
mergeSort ( nums, left, mid, temp) ;
mergeSort ( nums, mid + 1 , right, temp) ;
merge ( nums, left, mid, right, temp) ;
}
private void merge ( int [ ] nums, int left, int mid, int right, int [ ] temp) {
int i = left;
int j = mid + 1 ;
int index = left;
while ( i <= mid && j <= right) {
if ( nums[ i] <= nums[ j] ) {
temp[ index++ ] = nums[ i++ ] ;
} else {
temp[ index++ ] = nums[ j++ ] ;
count += mid - i + 1 ;
}
}
while ( j <= right) temp[ index++ ] = nums[ j++ ] ;
while ( i <= mid) temp[ index++ ] = nums[ i++ ] ;
index = left;
while ( left <= right) {
nums[ left++ ] = temp[ index++ ] ;
}
}
}
JZ52 两个链表的第一个公共节点
两个链表相交,则将两个链表按尾部对齐,其相交节点在同一位置,因此需要先分别遍历两个链表得到其长度,再次遍历时比较相应位置的指针即可
public class Solution {
public ListNode getIntersectionNode ( ListNode headA, ListNode headB) {
int lenA = 1 ;
int lenB = 1 ;
ListNode curA = headA;
ListNode curB = headB;
while ( curA != null ) {
lenA++ ;
curA = curA. next;
}
while ( curB != null ) {
lenB++ ;
curB = curB. next;
}
if ( lenA < lenB) {
curA = headB;
curB = headA;
int temp = lenA;
lenA = lenB;
lenB = temp;
} else {
curA = headA;
curB = headB;
}
while ( lenA > lenB) {
curA = curA. next;
lenA-- ;
}
while ( curA != null ) {
if ( curA == curB) return curA;
curA = curA. next;
curB = curB. next;
}
return null ;
}
}
两个链表相交,两个指针分别从两个链表头出发,逐个比较并向后移动,若两个指针不同时为null,则将null指针指向另一个链表头继续向后移动,直到两个指针同时为null
public class Solution {
public ListNode getIntersectionNode ( ListNode headA, ListNode headB) {
if ( headA == null || headB == null ) {
return null ;
}
ListNode curA = headA;
ListNode curB = headB;
while ( curA != null || curB != null ) {
if ( curA == null ) curA = headB;
if ( curB == null ) curB = headA;
if ( curA == curB) {
return curA;
}
curA = curA. next;
curB = curB. next;
}
return null ;
}
}