劍指offer第二週
24.機器人的運動範圍
地上有一個 mm 行和 nn 列的方格,橫縱座標範圍分別是 0∼m−10∼m−1 和 0∼n−10∼n−1。
一個機器人從座標0,0的格子開始移動,每一次只能向左,右,上,下四個方向移動一格。
但是不能進入行座標和列座標的數位之和大於 kk 的格子。
請問該機器人能夠達到多少個格子?
樣例1
輸入:k=7, m=4, n=5
輸出:20
樣例2
輸入:k=18, m=40, n=40
輸出:1484
解釋:當k爲18時,機器人能夠進入方格(35,37),因爲3+5+3+7 = 18。
但是,它不能進入方格(35,38),因爲3+5+3+8 = 19。
注意:
0<=m<=50
0<=n<=50
0<=k<=100
dfs, 如果機器人當前位置可以去,則去判斷上下左右能不能去;
class Solution {
int ans = 0;
boolean[][] vis;
public int movingCount(int threshold, int rows, int cols){
boolean[][] vis = new boolean[rows][cols];
// System.out.println(rows + " " + cols);
dfs(threshold,0,0,rows,cols);
return ans;
}
public void dfs(int k, int x,int y,int rows,int cols){
// System.out.println(x + " " + y);
if(x >= rows || y >= cols || x < 0 || y < 0 || get(x,y) > k || vis[x][y])return ;
else{
vis[x][y] = true;
ans ++;
dfs(k,x + 1,y,rows,cols);
dfs(k,x - 1,y,rows,cols);
dfs(k,x,y + 1,rows,cols);
dfs(k,x,y - 1,rows,cols);
}
}
public int get(int i ,int j){
int res = 0;
while(i > 0){
res += (i % 10);
i /= 10;
}
while(j > 0){
res += (j % 10);
j /= 10;
}
return res;
}
}
25.減繩子
給你一根長度爲 nn 繩子,請把繩子剪成 mm 段(mm、nn 都是整數,2≤n≤582≤n≤58 並且 m≥2m≥2)。
每段的繩子的長度記爲k[0]、k[1]、……、k[m]。k[0]k[1] … k[m] 可能的最大乘積是多少?
例如當繩子的長度是8時,我們把它剪成長度分別爲2、3、3的三段,此時得到最大的乘積18。
樣例
輸入:8
輸出:18
暴力,枚舉;
class Solution {
public int maxProductAfterCutting(int length)
{
int maxLen = length / 2;
int res = 1;
for(int i = 2; i <= maxLen; i ++){
res = Math.max(res, cut(length,i));
}
return res;
}
// 每次 選取中間的數乘,必定最大;
public int cut(int length,int cnt){
int ans = 1;
while(cnt > 0){
int avg = length / cnt;
ans *= avg;
cnt --;
length -= avg;
}
return ans;
}
}
y總說小學生題:可我還是暴力做的;
結論:每個數能取3,則取三,否則取2;
證明:N = n1 + n2 + n3 + … nk + …nN;
當n1 >= 5時,必定可以分成 3 * (n1 - 3) > n1;
n1 = 4時 ,n1 = 2*2;
不會有1的情況;
class Solution {
public int maxProductAfterCutting(int n)
{
if(n == 2)return 1;
int res = 1;
if(n % 3 == 1){
res *= 4;
n -= 4;
}
if(n % 3 == 2){
res *= 2;
n -= 2;
}
while(n >= 3){
res *= 3;
n -= 3;
}
return res;
}
}
26.二進制中1的個數
輸入一個32位整數,輸出該數二進制表示中1的個數。
注意:
- 負數在計算機中用其絕對值的補碼來表示。
樣例1
輸入:9
輸出:2
解釋:9的二進制表示是1001,一共有2個1。
樣例2
輸入:-2
輸出:31
解釋:-2在計算機裏會被表示成11111111111111111111111111111110,
一共有31個1。
樹狀數組中的lowbit,返回最後一個1的位置。(利用計算機中負數的補碼)
也可以利用 x & (x-1) 直接
class Solution {
public int NumberOf1(int n)
{
int res = 0;
while(n != 0){
n = n & (n - 1);// 直接把最後一個1剪掉,那麼&的時候就消失啦,看各位看官喜歡哪種;
// n -= lowbit(n); 同理
res ++;
}
return res;
}
public int lowbit(int n){
return n & (-n);
}
}
27.數值的整數次方
實現函數double Power(double base, int exponent),求base的 exponent次方。
不得使用庫函數,同時不需要考慮大數問題。
注意:
- 不會出現底數和指數同爲0的情況
- 當底數爲0時,指數一定爲正
樣例1
輸入:10 ,2
輸出:100
樣例2
輸入:10 ,-2
輸出:0.01
經典快速冪
class Solution {
public double Power(double base, int e) {
double res = 1;
long mi = e;
if(e < 0){
mi = -e;
base = 1 / base;
}
while(mi > 0){
if((mi & 1) == 1) res *= base;
base *= base;
mi >>= 1;
}
return res;
}
}
28.在O(1)時間刪除鏈表結點
給定單向鏈表的一個節點指針,定義一個函數在O(1)時間刪除該結點。
假設鏈表一定存在,並且該節點一定不是尾節點。
樣例
輸入:鏈表 1->4->6->8
刪掉節點:第2個節點即6(頭節點爲第0個節點)
輸出:新鏈表 1->4->8
由於此題爲單鏈表,無法得到前驅節點,故要刪除此節點,將下一個節點的值賦給自己,並且刪除下一個節點的值
class Solution {
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
}
29.刪除鏈表中重複的節點
在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留。
樣例1
輸入:1->2->3->3->4->4->5
輸出:1->2->5
樣例2
輸入:1->1->1->2->3
輸出:2->3
本菜雞,pre,cur,next利用三節點刪除代碼。(冗餘)
class Solution {
public ListNode deleteDuplication(ListNode head) {
ListNode dump = new ListNode(-1);
dump.next = head;
ListNode temp = head,pre = dump;
while(temp != null ){
ListNode cur = temp.next;
if(cur != null && temp.val == cur.val){
while(cur != null && temp.val == cur.val){
cur = cur.next;
}
pre.next = cur;
// pre = temp;
temp = cur;
}else{
pre = temp;
temp = cur;
}
}
return dump.next;
}
}
y總整潔代碼,以啞節點代替pre,判斷next走了多少步,如果只走一步,則代表不用刪,如果多走了則必定有相同元素,刪;
class Solution {
public ListNode deleteDuplication(ListNode head) {
ListNode dump = new ListNode(-1);
dump.next = head;
ListNode temp = dump;
while(temp.next != null ){
ListNode cur = temp.next;
while(cur != null && temp.next.val == cur.val){
cur = cur.next;
}
if(temp.next.next == cur)temp = temp.next;
else temp.next = cur;
}
return dump.next;
}
}
32.調整數組順序使奇數位於偶數前面
輸入一個整數數組,實現一個函數來調整該數組中數字的順序。
使得所有的奇數位於數組的前半部分,所有的偶數位於數組的後半部分。
樣例
輸入:[1,2,3,4,5]
輸出: [1,3,5,2,4]
利用快排的原理
class Solution {
public void reOrderArray(int [] array) {
int i = 0, j = array.length - 1;
while(i < j){
while(i < j && (array[i] & 1) == 1)i++;
while(j > i && (array[j] & 1) == 0)j--;
if(i < j){
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
}
33.鏈表中倒數第k個節點
輸入一個鏈表,輸出該鏈表中倒數第k個結點。
注意:
k >= 0
;- 如果k大於鏈表長度,則返回 NULL;
樣例
輸入:鏈表:1->2->3->4->5 ,k=2
輸出:4
經典快慢指針
class Solution {
public ListNode findKthToTail(ListNode head, int k) {
ListNode slow = head,fast = head;
while(k-- > 0){
if(fast == null)return null;
fast = fast.next;
}
while(fast != null){
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
34.鏈表中環的入口節點
給定一個鏈表,若其中包含環,則輸出環的入口節點。
若其中不包含環,則輸出null
。
樣例
給定如上所示的鏈表:
[1, 2, 3, 4, 5, 6]
2
注意,這裏的2表示編號是2的節點,節點編號從0開始。所以編號是2的節點就是val等於3的節點。
則輸出環的入口節點3.
由ListNode結構體得出,此鏈爲單鏈表,單鏈表最多隻會有一個環,故可判斷,當一個點被重複走過時,此點爲環的入口(入口會被最先經過兩次)
故:哈希做法 時間複雜度O(n),空間O(n)
class Solution {
Set<ListNode> set = new HashSet<>();
public ListNode entryNodeOfLoop(ListNode head) {
ListNode temp = head;
while(temp != null){
if(set.isEmpty() || !set.contains(temp))set.add(temp);
else return temp;
temp = temp.next;
}
return null;
}
}
最優解:雙鏈表 時間複雜度O(n),空間O(1)
假設它們相遇的時候爲y,起點到環入口爲x,那麼快指針走了2*(x+y) 慢指針走(x+y)
得出快指針在環內走x+2y ,減去離環起點的距離y,故x+y 一定是環的整數倍
所以從相遇點c,再走x步,一定能到起點b;
class Solution {
public ListNode entryNodeOfLoop(ListNode head) {
ListNode slow = head,fast = head;
while(fast != null && slow != null){
slow = slow.next;
fast = fast.next;
// 防止 無環情況下 NullPointerException
if(fast != null)fast = fast.next;
else return null;
// 當成環,讓fast 再走 x步,回到起點(x+y)是圈的整數倍
if(slow == fast){
slow = head;
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}
}