AcWing 24. 机器人的运动范围

地上有一个 m 行和 n 列的方格,横纵坐标范围分别是 0∼m−1 和 0∼n−1。

一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格。

但是不能进入行坐标和列坐标的数位之和大于 k 的格子。

请问该机器人能够达到多少个格子?

样例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

时间复杂度:《剑指Offer》第12-22题_补码
宽搜与深搜(容易爆栈)

class Solution(object):
def bfs(self, x, y, k):
def f(x, y):
res = 0
for i in str(x):
res += int(i)
for j in str(y):
res += int(j)
return res
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]
hh = 0
tt = -1
q = [[] for i in range(self.cols * self.rows + 100)]
tt += 1
q[tt] = [0, 0]
res = 0

while hh <= tt:

t = q[hh]

hh += 1
if self.st[t[0]][t[1]] == 1:
continue
self.st[t[0]][t[1]] = 1
res += 1

for i in range(4):
x1 = t[0] + dx[i]
y1 = t[1] + dy[i]
if x1 < self.rows and x1 >= 0 and y1 < self.cols and y1 >=0 and f(x1, y1) <= k and self.st[x1][y1] == 0:
tt += 1
q[tt] = [x1, y1]

return res
def movingCount(self, threshold, rows, cols):
"""
:type threshold: int
:type rows: int
:type cols: int
:rtype: int
"""
self.w = 0
self.st = [[0 for j in range(cols)] for i in range(rows)]
if rows == 0 or cols ==0 :
return 0
self.rows = rows
self.cols = cols
return self.bfs(0, 0, threshold)

深搜

class Solution(object):
def dfs(self, x, y, k):
self.w += 1
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]

res = 1
for t in range(4):
x1 = dx[t] + x
y1 = dy[t] + y
#
#if self.w == 100:
# return 0;
def f(x, y):
res = 0
for i in str(x):
res += int(i)
for j in str(y):
res += int(j)
return res
if x1 < self.rows and x1 >= 0 and y1 < self.cols and y1 >=0 and f(x1, y1) <= k and self.st[x1][y1] == 0:
# print(x1, y1)
self.st[x1][y1] = 1
res += self.dfs(x1, y1, k)
# self.st[x1][y1] = 0

return res
def movingCount(self, threshold, rows, cols):
"""
:type threshold: int
:type rows: int
:type cols: int
:rtype: int
"""
self.w = 0
self.st = [[0 for j in range(cols)] for i in range(rows)]
if rows == 0 or cols ==0 :
return 0
self.rows = rows
self.cols = cols
self.st[0][0] = 1
return self.dfs(0, 0, threshold)

AcWing 25. 剪绳子

给你一根长度为 n 绳子,请把绳子剪成 m 段(m、n 都是整数,2≤n≤58 并且 m≥2)。

每段的绳子的长度记为k[0]、k[1]、……、k[m]。k[0]k[1] … k[m] 可能的最大乘积是多少?

例如当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到最大的乘积18。

样例
输入:8

输出:18

算法
(数学) O(n)

这道题目是数学中一个很经典的问题。
下面我们给出证明:

首先把一个正整数 N拆分成若干正整数只有有限种拆法,所以存在最大乘积。
假设 ​​​N=n1+n2+…+nk​​​,并且 ​​n1×n2×…×nk​​是最大乘积。

  • 显然1不会出现在其中;
  • 如果对于某个 ​​i​​​有 ​​ni≥5​​​,那么把 ni 拆分成 ​​3 + (ni−3)​​​,我们有 ​​3(ni−3) = 3ni−9 > ni​​​;推出不能有大于​​5​​的数。
  • 如果 ​​ni=4​​​,拆成 ​​2+2​​​乘积不变,所以不妨假设没有​​4​​;
  • 如果有三个以上的2,那么 ​​3×3>2×2×2​​​,所以替换成3乘积更大;
    综上,选用尽量多的3,直到剩下2或者4时,用2

时间复杂度分析:当 n比较大时,n 会被拆分成 ​​⌈n/3⌉​​​ 个数,我们需要计算这么多次减法和乘法,所以时间复杂度是 ​​O(n)​​。

class Solution(object):
def maxProductAfterCutting(self,length):
"""
:type length: int
:rtype: int
"""
res = 1
if length % 3 == 0:
while length > 0:
length -= 3
res *= 3
elif length % 3 == 1:
length -= 4
while length > 0:
length -= 3
res *= 3
res *= 4
elif length % 3 == 2:
length -= 2
if length == 0:
return 1
while length > 0:
length -= 3
res *= 3
res *= 2
return res

AcWing 26. 二进制中1的个数

正数补码是本身,
负数补码 :符号位不变,逐位求反(从头开始), 加1。

思路1:
lowbit : n&(n-1)的结果:n最右边的1变成0,比如n为6
110&101-》100
循环直到n为0为止
上述思想C++可以直接过。
为什么python中负数需要和 0xFFFFFFFF 做与操作?
在计算机中,所有的数字都是使用补码存储起来的。由于Python没有位数这个概念,所以得到二进制表示需要多一点操作,即将位数限制在32位,通过和一个32位的全1数字按位与运算即可。但对于负数来说,直接bin(-1)是不能得到其补码的,而是得到了1的原码前面加上了负号,即-0b1。则通过和一个32位的全1数字按位与运算可得到其补码二进制表示对应的十进制数(按位与运算把符号位的1视为了数字)。Python要使用 n & 0xffffffff 得到一个数的补码

class Solution(object):
def NumberOf1(self,n):
"""
:type n: int
:rtype: int
"""
res = 0
if n < 0:
n = n & 0xFFFFFFFF
while n != 0 :
n -= n & (-n)
res += 1


return res

思路2
(位运算) ​​​O(logn)​​ 迭代进行如下两步,直到 n 变成0为止:

如果 n在二进制表示下末尾是1,则在答案中加1;
将 n右移一位,也就是将 nn 在二进制表示下的最后一位删掉;
这里有个难点是如何处理负数。
在C++中如果我们右移一个负整数,系统会自动在最高位补1,这样会导致 n 永远不为0,就死循环了。
解决办法是把 n 强制转化成无符号整型,这样 n 的二进制表示不会发生改变,但在右移时系统会自动在最高位补0。

时间复杂度
每次会将 n除以2,最多会除 logn 次,所以时间复杂度是 O(logn)。

class Solution {
public:
int NumberOf1(int n) {
int res = 0;
unsigned int un = n;
while (un) res += un & 1, un >>= 1;
return res;
}
};

AcWing 27. 数值的整数次方

实现函数double Power(double base, int exponent),求base的 exponent次方。

不得使用库函数,同时不需要考虑大数问题。

注意:

不会出现底数和指数同为0的情况
当底数为0时,指数一定为正

样例1 输入:10 ,2
输出:100

样例2
输入:10 ,-2 >
输出:0.01

注意一下负数即可

class Solution(object):
def Power(self, base, exponent):
"""
:type base: float
:type exponent: int
:rtype: float
"""
b = exponent
exponent = abs(exponent)
res = 1
while exponent > 0:
if exponent & 1 == 1:
res *= base

base = base * base
exponent >>= 1

if b < 0:
res = 1 / res
return res

AcWing 28. 在O(1)时间删除链表结点

算法
(链表) O(1)

给定单向链表的一个节点指针,定义一个函数在O(1)时间删除该结点。

假设链表一定存在,并且该节点一定不是尾节点。

样例 输入:链表 1->4->6->8
删掉节点:第2个节点即6(头节点为第0个节点)

输出:新链表 1->4->8
由于是单链表,我们不能找到前驱节点,所以我们不能按常规方法将该节点删除。
我们可以换一种思路,将下一个节点的值复制到当前节点,然后将下一个节点删除即可。

时间复杂度
只有常数次操作,所以时间复杂度是 O(1)。

# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution(object):
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void
"""
node.val = node.next.val
node.next = node.next.next

AcWing 29. 删除链表中重复的节点

算法
(线性扫描) O(n)

为了方便处理边界情况,我们定义一个虚拟元素 dummy 指向链表头节点。
然后从前往后扫描整个链表,每次扫描元素相同的一段,如果这段中的元素个数多于1个,则将整段元素直接删除。

时间复杂度
整个链表只扫描一遍,所以时间复杂度是 O(n)。

# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution(object):
def deleteDuplication(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
dumpy = ListNode(None)
dumpy.next = head
p = dumpy
while p != None:
q = p.next
while p.next != None and q.next != None and p.next.val == q.next.val:
q = q.next

if p.next != q:
p.next = q.next
else: p = p.next

return dumpy.next
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* head) {
auto dummy = new ListNode(-1);
dummy->next = head;

auto p = dummy;
while (p->next) {
auto q = p->next;
while (q && p->next->val == q->val) q = q->next;

if (p->next->next == q) p = p->next;
else p->next = q;
}

return dummy->next;
}
};

AcWing 30. 正则表达式匹配

题目描述
请实现一个函数用来匹配包括’.’和’*’的正则表达式。

模式中的字符’.’表示任意一个字符,而’*’表示它前面的字符可以出现任意次(含0次)。

在本题中,匹配是指字符串的所有字符匹配整个模式。

例如,字符串”aaa”与模式”a.a”和”abaca”匹配,但是与”aa.a”和”ab*a”均不匹配。

样例 输入:

s=“aa” p=“a*”

输出:true

算法
(动态规划) O(nm)

状态表示:f[i][j]为所有s[1…i]与p[1…j]匹配的方案的集合。
属性:集合是否为空
状态转移:

  1. 如果p[j]不是通配符’*’
    《剑指Offer》第12-22题_补码_02
  2. 如果p[j+1]是通配符’​​*'​​,*表示字母个数来划分集合
  • 当​​*​​​表示的字母个数为0个时:
    《剑指Offer》第12-22题_时间复杂度_03
  • 当​​*​​​表示的字母个数为1个时:
    《剑指Offer》第12-22题_算法_04
  • 当​​*​​​表示的字母个数为2个时:
    《剑指Offer》第12-22题_补码_05

类似完全背包的优化方式
《剑指Offer》第12-22题_时间复杂度_06
《剑指Offer》第12-22题_链表_07
后半部都多了《剑指Offer》第12-22题_补码_08,结合律(A&b|A&c=A&(b|c))即可推出《剑指Offer》第12-22题_链表_09
时间复杂度分析:
n表示s的长度,m 表示p的长度,总共 nm个状态,状态转移复杂度 O(1),所以总时间复杂度是 O(nm).

《剑指Offer》第12-22题_补码_10

class Solution(object):
def isMatch(self, s, p):
"""
:type s: str
:type p: str
:rtype: bool
"""
n = len(s)
m = len(p)
s = " " + s
p = " " + p
dp = [[False for j in range(m + 1)] for i in range(n + 1)]
dp[0][0] = True

for i in range(0, n + 1):
for j in range(1, m + 1):
if j + 1 < m and p[j+1] == "*":
continue

if i and p[j] != "*":
dp[i][j] = dp[i - 1][j - 1] and (s[i] == p[j] or p[j] == '.')
elif p[j] == '*':
dp[i][j] = dp[i][j - 2] or (i and dp[i - 1][j] and (s[i] == p[j - 1] or p[j - 1] == '.'))

return dp[n][m]
class Solution(object):
def isMatch(self, s, p):
"""
:type s: str
:type p: str
:rtype: bool
"""
n = len(s)
m = len(p)
f = [[False] * (m+1) for i in range(n+1)]
s = ' ' + s
p = ' ' + p
f[0][0] = True

for i in range(n+1):
for j in range(1, m+1):
if j + 1 < m and p[j+1] == '*':
continue
if i and p[j] != '*':
if s[i] == p[j] or p[j] == '.':
f[i][j] = f[i-1][j-1]
elif p[j] == '*':
f[i][j] = f[i][j-2] or (f[i-1][j] and (s[i] == p[j-1] or p[j-1] == '.'))
return f[n][m]

AcWing 31. 表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。

例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。

但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

注意:

  1. 小数可以没有整数部分,例如.123等于0.123;
  2. 小数点后面可以没有数字,例如233.等于233.0;
  3. 小数点前面和后面可以有数字,例如233.666;
  4. 当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
  5. 当e或E后面没有整数时,整个字符串不能表示数字,例如12e、12e+5.4;

样例: 输入: “0”

输出: true

class Solution(object):
def isNumber(self, s):
"""
:type s: str
:rtype: bool
"""
try :
float(s)
return True
except:
return False

AcWing 32. 调整数组顺序使奇数位于偶数前面

算法
(双指针扫描) O(n)

用两个指针分别从首尾开始,往中间扫描。扫描时保证第一个指针前面的数都是奇数,第二个指针后面的数都是偶数。

每次迭代时需要进行的操作:

  • 第一个指针一直往后走,直到遇到第一个偶数为止;
  • 第二个指针一直往前走,直到遇到第一个奇数为止;
  • 交换两个指针指向的位置上的数,再进入下一层迭代,直到两个指针相遇为止;

时间复杂度
当两个指针相遇时,走过的总路程长度是 n,所以时间复杂度是 O(n)。

class Solution(object):
def reOrderArray(self, array):
"""
:type array: List[int]
:rtype: void
"""
i = 0
j = len(array) - 1
while i < j:
while i< j and array[i] & 1 == 1:
i += 1

while j > i and array[j] & 1 != 1:
j -= 1
if i <j : array[i], array[j] = array[j], array[i]

AcWing 33. 链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个结点。

注意:

  • k >= 0; 如果k大于链表长度,则返回 NULL;

样例

输入:链表:1->2->3->4->5 ,k=2

输出:4

# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution(object):
def findKthToTail(self, pListHead, k):
"""
:type pListHead: ListNode
:type k: int
:rtype: ListNode
"""
p = pListHead
n = 1
while p != None:
n += 1
p = p.next

if k > n:
return None
p = pListHead
i = 1

while p != None:
if i == n-k:
return p
p = p.next
i += 1

AcWing 34. 链表中环的入口结点

给定一个链表,若其中包含环,则输出环的入口节点。

若其中不包含环,则输出null。

样例

《剑指Offer》第12-22题_链表_11

给定如上所示的链表:

[1, 2, 3, 4, 5, 6]
2
注意,这里的2表示编号是2的节点,节点编号从0开始。所以编号是2的节点就是val等于3的节点。

则输出环的入口节点3.

算法
(链表,快慢指针扫描) O(n)

用两个指针 first,second 分别从起点开始走,first 每次走一步,second 每次走两步。
如果过程中 second走到null,则说明不存在环。否则当 first 和 second相遇后,让 first返回起点,second 待在原地不动,然后两个指针每次分别走一步,当相遇时,相遇点就是环的入口。

《剑指Offer》第12-22题_补码_12


证明:如上图所示,a 是起点,b 是环的入口,c 是两个指针的第一次相遇点,ab 之间的距离是 x,bc 之间的距离是 y, z表示从 cc点顺时针走到 b 的距离。

则第一次相遇时 second所走的距离是 《剑指Offer》第12-22题_时间复杂度_13, n表示圈数,同时 second 走过的距离是 first 的两倍,也就是 《剑指Offer》第12-22题_时间复杂度_14,所以我们有《剑指Offer》第12-22题_算法_15,所以 《剑指Offer》第12-22题_补码_16

y+z是一圈的距离,z = x %(y+z),那么我们让 second从 c 点开始走,走 x步,会恰好走到 b 点;让 first 从 a 点开始走,走 x 步,也会走到 b 点。

# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution(object):
def entryNodeOfLoop(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
first = head
second = head

while first and second:
if first == None or second == None:
return None
first = first.next
second = second.next
if second :
second = second.next
if first == second:
first = head
while first and second:
if first == second:
return first
first = first.next
second = second.next
return None

map o(nlogn) 空间o(n)

class Solution {
public:
ListNode *entryNodeOfLoop(ListNode *head) {
map<ListNode*,int> mp;
while(head)
{
if(mp[head])
return head;
mp[head]=1;
head=head->next;
}
return NULL;
}
};