1 找出数组中重复的数字
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
思路:哈希
建立一个哈希表,遍历数组,如果访问到同一个哈希表的同一个位置就返回。
int findRepeatNumber(int* nums, int numsSize){
int *hash = (int *)calloc(numsSize, sizeof(int));
for(int i = 0; i < numsSize; i++){
if(hash[nums[i]] == 1){
return nums[i];
} else {
hash[nums[i]]++;
}
}
return -1;
}
class Solution:
def findRepeatNumber(self, nums: List[int]) -> int:
dit = set()
for num in nums:
if num in dit:
return num
else:
dit.add(num)
return -1
法二:排序
int cmp(const void *_a, const void *_b)
{
int *a = (int * )_a;
int *b = (int * )_b;
return *a - *b;
}
int findRepeatNumber(int* nums, int numsSize){
int res;
qsort(nums, numsSize, sizeof(int), cmp);
for(int i = 0; i < numsSize; i++){
if(nums[i] == nums[i+1]){
res = nums[i];
break;
}
}
return res;
}
class Solution:
def findRepeatNumber(self, nums: List[int]) -> int:
nums.sort()
for i in range(len(nums)-1):
if nums[i] == nums[i+1]:
return nums[i]
2 找出数组中的最小值(学习二分查找)
方法一:排序
int cmp(const void *a, const void *b){
return *(int *)a - *(int *)b;
}
int minArray(int* numbers, int numbersSize){
qsort(numbers, numbersSize, sizeof(int), cmp);
return numbers[0];
}
class Solution:
def minArray(self, numbers: List[int]) -> int:
numbers.sort()
return numbers[0]
方法二:查找
二分查找(***)
int minArray(int* numbers, int numbersSize){
int low = 0, high = numbersSize-1;
while(low < high){
int mid = low + (high - low) / 2;
if(numbers[mid] < numbers[high]){
high = mid;
} else if (numbers[mid] > numbers[high]) {
low = mid + 1;
} else {
high -=1;
}
}
return numbers[low];
}
class Solution:
def minArray(self, numbers: List[int]) -> int:
low, high = 0, len(numbers) -1
while low < high:
mid = int(low + (high - low) / 2)
if numbers[mid] < numbers[high]:
high = mid
elif numbers[mid] > numbers[high]:
low = mid + 1
else:
return min(numbers[low:high])
return numbers[low]
class Solution:
def minArray(self, numbers: [int]) -> int:
i, j = 0, len(numbers) - 1
while i < j:
m = (i + j) // 2
if numbers[m] > numbers[j]: i = m + 1
elif numbers[m] < numbers[j]: j = m
else: j -= 1
return numbers[i]
暴力查找
int minArray(int* numbers, int numbersSize){
int min = numbers[0];
for(int i=1; i < numbersSize; i++){
if(min > numbers[i])
min = numbers[i];
}
return min;
}
class Solution:
def minArray(self, numbers: List[int]) -> int:
min = numbers[0]
for num in numbers:
if min > num:
min = num
return min
3 两数之和(学习二分查找)
双指针
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
*returnSize=2;
int *array=(int*)malloc(sizeof(int)*2);
int left=0;
int right=numsSize-1;
while(left<right){
if(nums[left]+nums[right]>target){
--right;
}
else if(nums[left]+nums[right]<target){
++left;
} else {
array[0]=nums[left];
array[1]=nums[right];
return array;
}
}
return NULL;
}
二分查找
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
if(numsSize==1)return 0;
if(numsSize==2 && nums[0]+nums[1]!=target)return 0;
int* a=(int*)malloc(sizeof(int)*2);
for(int i=0;i<numsSize;i++){
a[0]=nums[i];
int x=target-a[0];
int left=i+1,right=numsSize-1;
while(left<=right){
int mid=(right+left)/2;
if(x==nums[mid]){
a[1]=nums[mid];
break;
}
if(x<nums[mid]){
right=mid-1;
}else{
left=mid+1;
}
}
if(a[0]+a[1]==target)break;
}
*returnSize=2;
return a;
}
排序数组中的两数之和(哈希)
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
dic = {}
for i,num in enumerate(numbers):
if (target - num) in dic:
return [dic[target - num], i]
dic[num] = i
4 打印从1到最大的n位数
int* printNumbers(int n, int* returnSize) {
int N = pow(10, n) - 1;
int *res = (int *)malloc(sizeof(int) * N);
res[0] = 1;
for(int i = 1; i < N; i++) {
res[i] = res[i-1] + 1;
}
*returnSize = N;
return res;
}
class Solution:
def printNumbers(self, n: int) -> List[int]:
return [i for i in range(1, 10**n)]
class Solution:
def printNumbers(self, n: int) -> List[int]:
res = []
for i in range(1, 10 ** n):
res.append(i)
return res
考虑数据很大的情况
( 不好理解,debug调试勉强了解过程 )
class Solution:
def printNumbers(self, n: int) -> List[int]:
def dfs(index, num, digit):
if index == digit:
res.append(int(''.join(num)))
return
for i in range(10):
num.append(str(i))
dfs(index + 1, num, digit)
num.pop()
res = []
for digit in range(1, n + 1):
for first in range(1, 10):
num = [str(first)]
dfs(1, num, digit)
return res
5 将数组中的奇数置于前半部分偶数置于后半部分
方法一:创建新数组,便利原数组将奇数和偶数分别按顺序装进新数组
int* exchange(int* nums, int numsSize, int* returnSize){
*returnSize = numsSize;
int left = 0;
int right = numsSize - 1;
int *res = (int *)malloc(sizeof(int)*numsSize);
for(int i=0; i < numsSize; i++){
if(nums[i] % 2 == 0) {
res[right--] = nums[i];
} else {
res[left++] = nums[i];
}
}
return res;
}
方法二:双指针,前后指针,前指针找到最近的偶数,后指针找到最近的奇数,交换。
int* exchange(int* nums, int numsSize, int* returnSize){
*returnSize = numsSize;
int left = 0;
int right = numsSize - 1;
int t;
while(left < right){
while(left < right && nums[left] % 2 == 1){
left ++;
}
while(left < right && nums[right] % 2 == 0){
right --;
}
if(left < right)
{
t = nums[left];
nums[left] = nums[right];
nums[right] = t;
}
}
return nums;
}
6 找出数组中出现次数超过数组长度一半的数
方法一:排序,返回数组中间的数
int cmp (const void *a, const void *b){
return *(int *)a - *(int *)b;
}
int majorityElement(int* nums, int numsSize){
qsort(nums, numsSize, sizeof(int), cmp);
return nums[numsSize/2];
}
方法二:哈希映射
int majorityElement(int* nums, int numsSize){
int hash[20000] = {0}, i = 0, j = 0;
for(i = 0; i < numsSize; i++)
hash[nums[i] % INT_MAX + 10000]++;
for(j = 0; j < numsSize; j++)
if(hash[nums[j] % INT_MAX + 10000] > numsSize/2) break;
return nums[j];
}
方法三:摩尔投票
定于一个候选众数和一个计数CNT,遍历数组,首先判断CNT==0?如果是将此时遍历到的数作为候选众数,根据此时遍历到的数是否等于候选众数来判断CNT加一还是减一。
int majorityElement(int* nums, int numsSize){
int cnt = 0;
int a = 0;
for(int i = 0 ; i < numsSize; i++){
if(cnt == 0){
a = nums[i];
}
cnt += (a == nums[i]? 1:-1);
}
return a;
}
我们维护一个候选众数 candidate 和它出现的次数 count。初始时 candidate 可以为任意值,count 为 0;
我们遍历数组 nums 中的所有元素,对于每个元素 x,在判断 x 之前,如果 count 的值为 0,我们先将 x 的值赋予 candidate,
随后我们判断 x:
如果 x 与 candidate 相等,那么计数器 count 的值增加 1;
如果 x 与 candidate 不等,那么计数器 count 的值减少 1。
在遍历完成后,candidate 即为整个数组的众数。
7 顺时针打印矩阵
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
思路一:将矩阵的四个角的坐标分别设置为【top, left】【top, right】【button, left】【button, right】
在输出的时候我们就可以顺着这样的顺序来打印,第一步从【top, left】到【top, right】第二步从【top+1, right】【button, right】
第三步从 【button, right-1】到【button, left】第四步从【button-1, left】到【top, left】,接着更新 top、button、left、right
int* spiralOrder(int** matrix, int matrixSize, int* matrixColSize, int* returnSize) {
if (matrixSize == 0 || matrixColSize[0] == 0) {
*returnSize = 0;
return NULL;
}
int rows = matrixSize, columns = matrixColSize[0];
int total = rows * columns;
int* order = malloc(sizeof(int) * total);
*returnSize = 0;
int left = 0, right = columns - 1, top = 0, bottom = rows - 1;
while (left <= right && top <= bottom) {
for (int column = left; column <= right; column++) {
order[(*returnSize)++] = matrix[top][column];
}
for (int row = top + 1; row <= bottom; row++) {
order[(*returnSize)++] = matrix[row][right];
}
if (left < right && top < bottom) {
for (int column = right - 1; column > left; column--) {
order[(*returnSize)++] = matrix[bottom][column];
}
for (int row = bottom; row > top; row--) {
order[(*returnSize)++] = matrix[row][left];
}
}
left++;right--;top++;bottom--;
}
return order;
}
思路二:模拟输出过程
在顺时针输出矩阵的时候,关键在于判断拐角元素,如果遇到拐角元素需要转换输出方向。
/* 定义了四行两列的数组,用来记录运行的行列变化,例如自左向右输出的时候,行是不变的,列每次增加1*/
int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int* spiralOrder(int** matrix, int matrixSize, int* matrixColSize, int* returnSize) {
if (matrixSize == 0 || matrixColSize[0] == 0) {
*returnSize = 0;
return NULL;
}
int rows = matrixSize, columns = matrixColSize[0];
int visited[rows][columns];
memset(visited, 0, sizeof(visited));
int total = rows * columns;
int* order = malloc(sizeof(int) * total);
*returnSize = total;
int row = 0, column = 0;
int directionIndex = 0;
for (int i = 0; i < total; i++) {
order[i] = matrix[row][column];
visited[row][column] = true; /* 遍历之后将遍历数组对应位置位 TRUE */
int nextRow = row + directions[directionIndex][0];
int nextColumn = column + directions[directionIndex][1];
if (nextRow < 0 || nextRow >= rows || nextColumn < 0 || nextColumn >= columns || visited[nextRow][nextColumn]) { /* 通过判断当前元素的下一行是否是最后一行或者最后一列以及是否是遍历过的元素来确定是否需要改变行进方向 */
/* 访问到了拐角元素需要进行方向转换 */
directionIndex = (directionIndex + 1) % 4;
}
row += directions[directionIndex][0];
column += directions[directionIndex][1];
}
return order;
}
8 数组中最小的 k 个数(学习快速排序)
思路:排序输出
实现一:利用qsort函数排序
int cmp (const int *a, const int *b) {
return *(int *)a - *(int *)b;
}
int* getLeastNumbers(int* arr, int arrSize, int k, int* returnSize) {
qsort(arr, arrSize, sizeof(int), cmp);
*returnSize = k;
int index = 0;
int *res = (int *)malloc(sizeof(int)*k);
for(int i = 0; i < k; i++) {
res[index++] = arr[i];
}
return res;
}
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
arr.sort()
return [arr[i] for i in range(k)]
实现二:利用快速排序排序
void swap(int* a ,int* b){
int tmp = *a; *a = *b; *b = tmp;
}
int partition(int* arr , int start , int end ){
int privot = arr[start];
while(start < end){
while(start < end && arr[end] >= privot){
end--;
}
if(start < end){
arr[start++] = arr[end];
}
while(start < end && arr[start] <= privot){
start++;
}
if(start < end){
arr[end--] = arr[start];
}
}
arr[start] = privot;
return start;
}
int* quickSort(int* arr , int start , int end , int k){
if(start < end){
int mid = partition(arr, start , end);
if(mid == k ){
return arr;
}
if(mid > k ){
return quickSort(arr, start , mid , k);
}
if(mid < k ){
return quickSort(arr, mid + 1 , end, k);
}
}
return arr;
}
int* getLeastNumbers(int* arr, int arrSize, int k, int* returnSize){
if(arr == NULL || arrSize < 0){
*returnSize = 0;
return NULL;
}
if(arrSize == 0 || k == 0 || arrSize < k){
*returnSize = 0;
return arr;
}
*returnSize = k;
return quickSort(arr, 0 , arrSize -1 , k );
}
# 使用排序算法
# 首先选定第一个元素作为基准,将比该元素小的成员移到左边,比他大的成员移到右边,在将左右序列重复该# 操作,最后输出前几位即为最小
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
def quick_sort(arr,l,r):
if l >= r: return
i, j = l, r
while i < j:
while i < j and arr[j] >= arr[l]: j-=1
while i < j and arr[i] <= arr[l]: i+=1
arr[i], arr[j] = arr[j], arr[i]
arr[l], arr[i] = arr[i], arr[l]
quick_sort(arr, l, i-1)
quick_sort(arr, i+1, r)
quick_sort(arr, 0, len(arr)-1)
return arr[:k]
# 由于对输出的k个数没有要求,只需要将数组划分为最小的 k 个数 和其他数字两部分即可,而快速排序的哨
# 兵划分可完成此目标。根据快速排序原理,如果某次哨兵划分后 基准数正好是第 k+1 小的数字 ,那么
# 此时基准数左边的所有数字便是题目所求的 最小的k个数。
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
if k >= len(arr): return arr
def quick_sort(l ,r):
i, j = l, r
while i < j:
while i < j and arr[j] >= arr[l]: j-=1
while i < j and arr[i] <= arr[l]: i+=1
arr[i], arr[j] = arr[j], arr[i]
arr[l], arr[i] = arr[i], arr[l]
if k < i: return quick_sort(l, i-1)
if k > i: return quick_sort(i+1, r)
return arr[:k]
return quick_sort(0, len(arr)-1)
9 扑克牌中的顺子
从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
思路:首先排序,遍历数组,统计0的个数并且如果有重复的非0值直接返回 FALSE,最关键的是 能否构成顺子的条件为 最大值 - 非0最小值 < 5.
int cmp(const int *a, const int *b){
return *(int *)a - *(int *)b;
}
bool isStraight(int* nums, int numsSize) {
int i=0, j;
qsort(nums, numsSize, sizeof(int), cmp);
for(j = 0; j < numsSize-1; j++) {
if(nums[j] == 0) {
i ++;
} else if(nums[j] == nums[j+1]) {
return false;
}
}
return nums[numsSize-1] - nums[i] < 5;
}
10 连续子数组的最大和
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。
思路:动态规划
状态定义:设动态规划列表 dp ,dp[ i ] 代表以元素 nums[ i ] 为结尾的连续子数组最大和
转移方程:如果 dp[ i-1] <= 0 也就是说遇到了负数那么我们就要舍弃掉
当 dp [ i−1 ] > 0 时:执行 dp [ i ] = dp [ i-1 ] + nums [ i ] ;
当 dp [ i−1 ] ≤ 0 时:执行 dp [ i ] = nums [ i ];
初始状态:dp[0]=nums[0],即以 nums[0] 结尾的连续子数组最大和为 nums[0]
返回值:返回dp列表中的最大值,代表全局的最大值
由于 dp 只与 dp[ i-1 ] 和 nums [ i ] 有关,因此可以将原始数组 nums 作为 dp,节省空间,即直接在 nums 上修改即可。
int maxSubArray(int* nums, int numsSize){
if (numsSize == 0)
return 0;
int ans = nums[0];
for (int i = 1; i < numsSize; i++){
nums[i] += fmax(nums[i-1], 0);
ans = fmax(ans, nums[i]);
}
return ans;
}
11 统计数组中某个元素出现的次数
思路一:遍历
int search(int* nums, int numsSize, int target){
int k =0;
for(int i = 0; i < numsSize; i++){
if(nums[i] == target){
k++;
}
}
return k;
}
思路二:二分查找
int search(int* nums, int numsSize, int target){
int left = 0, right = numsSize-1, mid, cnt = 0;
while(left <= right){
mid = (right + left)>>1;
if(nums[mid] == target){
for(int i=mid-1; i>=0 && nums[i]==target; i--){ // 向左查找target
cnt++;
}
for(int i=mid; i<numsSize && nums[i]==target; i++){ // 向右查找target
cnt++;
}
break;
}else if(nums[mid] < target){
left = mid + 1;
}else{
right = mid - 1;
}
}
return cnt;
}
12 0 ~ n-1 中缺失的数字
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
思路一:二分查找
int missingNumber(int* nums, int numsSize){
int left = 0, right = numsSize -1;
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] == mid){
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
思路二:遍历
int missingNumber(int* nums, int numsSize){
int i =0;
if(numsSize == 1 && nums[0] == 0){
return 1;
} else if(numsSize == 1 && ! nums[0]){
return 0;
}
for(i = 0; i < numsSize; i++){
if(nums[i] != i){
break;
}
}
return i;
}
13 数组中只有一个数字出现一次,其他每个数字都出现三次,找出只出现一次的数
思路一:位运算
在使用位运算的时候,我们思考一下,如何能使相同的三个数消除掉,由于二进制数每一位要么取一要么取零,那么相加之后的到的就是0/3,将每个数字的对应位相加得到的数在对三取余之后就得到的是只出现一次的数。
int singleNumber(int* nums, int numsSize){
int res = 0;
for(int i = 0; i < 32; i++){
int bit = 0;
for(int j = 0; j < numsSize; j++){
bit += (nums[j]>>i) & 1;
}
res += (bit % 3) << i ;
}
return res;
}
思路二:排序,查找
int cmp(const int *a, const int *b){
return *(int *)a - *(int *)b;
}
int singleNumber(int* nums, int numsSize){
qsort(nums, numsSize, sizeof(int), cmp);
if(nums[0]!=nums[1]){
return nums[0];
}
if(nums[numsSize-2]!=nums[numsSize-1]){
return nums[numsSize-1];
}
for(int i=1; i< numsSize-1; i++){
if(nums[i]!=nums[i-1] && nums[i]!=nums[i+1]){
return nums[i];
}
}
return 0;
}
思路三 字典
class Solution:
def singleNumber(self, nums: List[int]) -> int:
numbers = {}
for num in nums:
if str(num) not in numbers.keys():
numbers[str(num)] = 1
else:
numbers[str(num)] = int(numbers[str(num)])+1
for key in numbers.keys():
if numbers[key] == 1:
return int(key)
class Solution:
def singleNumber(self, nums: List[int]) -> int:
ref = collections.Counter(nums)
for key in ref.keys():
if ref[key]==1:
return key
14 数组中除两个数字出现一次之外,其他数字都出现两次,找出只出现一次的数
思考:此题中其他数字出现两次,我们同样采用位运算的方法,首先想到异或,异或的操作可以直接剔除掉出现两次的数字,最后得到的结果就是两个出现一次的数异或的结果,为了从异或结果中分别找出这两个数。我们先定位到异或结果中为“1”的位,同时在原数组中查找此位为1的数放在同一组,为0的数放在同一组,这样每一组中都包含了一个最后的结果。每一组中除我们需要的数之外都是两两相同的数,我们直接异或即可得到最后的结果。
int* singleNumbers(int* nums, int numsSize, int* returnSize){
* returnSize = 2;
int *res = (int *)malloc(sizeof(int)*2);
int a = 0, num1 = 0, num2 = 0, pos =0;
for(int i =0; i < numsSize; i++){
a = a ^ nums[i];
}
for (int j = 0; j < 32; j++){
if((a>>j)&1 == 1){
pos = j;
break;
}
}
for(int k = 0; k < numsSize; k++){
if((nums[k]>>pos) & 1 == 1){
num1 = num1 ^ nums[k];
} else {
num2 = num2 ^ nums[k];
}
}
res[0] = num1;
res[1] = num2;
return res;
}
13 礼物的最大价值
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
思路:由于只能向右或者向下走,所以我们先考虑第一行和第一列,用一个二维数组sum记录累计的值,当遇到非边缘元素时,他要么是从上面走下来的,要么是从左边走过来的,对于这样的元素我们比较上方和左边的sum值哪个更大即可,在加上这个位置的原来的数据的值作为此位置的sum数组的值即可。
int maxValue(int** grid, int gridSize, int* gridColSize)
{
int i, j;
int sum[gridSize][*gridColSize];
memset(sum, 0, sizeof(sum));
sum[0][0] = grid[0][0];
for(i = 1; i < gridSize; i ++){
sum[i][0] += grid[i][0] + sum[i-1][0];
}
for(i = 1; i < gridColSize[0]; i++){
sum[0][i] += grid[0][i] + sum[0][i-1];
}
for(i = 1; i < gridSize; i++){
for(j = 1; j < gridColSize[0]; j++){
sum[i][j] += fmax(sum[i-1][j], sum[i][j-1]) + grid[i][j];
}
}
return sum[gridSize-1][gridColSize[0]-1];
}
13 股票的最大利润(动态规划)
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
思路一:首先将数组中的最后一个数字定义为最大的利润,从最后一天的前一天开始向前遍历,计算数组中元素与 max 的差值,如果差值为负也即是说 max 较小,那么就将这个数定义为 max,如果这个差值大于0, 那么我们就比较差值与最大利润的大小,更新最大利润值。
int maxProfit(int* prices, int pricesSize){
int max_profit = 0, profit = 0;
if(pricesSize >= 2){
int max = prices[pricesSize-1];
for(int i = pricesSize-2; i >= 0; i--){
profit = max - prices[i];
if(profit <= 0){
max = prices[i];
} else if(profit > max_profit){
max_profit = profit;
}
}
}
return max_profit;
}
思路二:直接遍历
不建议,时间复杂度太高了
int maxProfit(int* prices, int pricesSize){
int prof = 0;
for(int i = 0; i < pricesSize; i++)
for(int j = i + 1; j < pricesSize; j++){
prof = fmax(prof, prices[j] - prices[i]);
}
return prof;
}
思路三:动态规划
先定义一个 cost 等于整型的最大值,接着遍历数组,将 cost 置为最小的数,来取当前元素中与 cost 差值最大的情况。
int maxProfit(int* prices, int pricesSize){
int cost = INT_MAX;
printf("%d",cost);
int prof = 0;
for (int i = 0; i < pricesSize; i++){
cost = fmin(cost, prices[i]);
prof = fmax(prof, prices[i] - cost);
}
return prof;
}
14 构建乘积数组
新建一个数组,新建数组中的元素是元素中除去该元素下标的所有其他元素的积。
思路:
int* constructArr(int* a, int aSize, int* returnSize) {
if (a == NULL || aSize < 2) {*returnSize = 0; return a;}
int *res = (int *)malloc(aSize * sizeof(int));
if (res == NULL ) {*returnSize = 0;return res;}
for(int i = 0; i < aSize; i++){
res[i] = 1;
}
for(int i = 1; i < aSize; i++){
res[i] = res[i-1] * a[i-1];
}
int temp = 1;
for(int i = aSize-2; i >= 0; i--){
temp *= a[i+1];
res[i] *= temp;
}
*returnSize = aSize;
return res;
}
15 二维数组的查找
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路一:暴力搜索
bool findNumberIn2DArray(int** matrix, int matrixSize, int* matrixColSize, int target){
if(matrixSize == 0 || *matrixColSize == 0)
return false;
for(int i= 0; i < matrixSize; i++){
for(int j = 0; j < *matrixColSize; j++){
if(matrix[i][j] == target){
return true;
}}}
return false;
}
思路二:一维搜索
由于给定的二维数组从行的角度和列的角度都是有序的,我们可以从右上角出发,观察右上角元素的值与目标值的大小,如果小于目标值就向下一行移动,如果大于目标值就向前一列移动。
bool findNumberIn2DArray(int** matrix, int matrixSize, int* matrixColSize, int target){
if(matrixSize == 0 || *matrixColSize == 0)
return false;
int i = 0, j = *matrixColSize -1;
while(i < matrixSize && j >= 0 ){
if(matrix[i][j] == target){
return true;
} else if(matrix[i][j] < target){
i ++;
} else {
j --;
}
}
return false;
}
16 栈的压入和弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
思路:构建一个空栈,将原栈序列入栈同时判断栈顶元素是否等于出栈序列,如果是,将新建栈中的元素出栈,并将 出栈序列的索引+1,最后判断新建栈是否是空栈,如果是则返回 TRUE 否则返回 FALSE。
bool validateStackSequences(int* pushed, int pushedSize, int* popped, int poppedSize){
if(pushedSize == 0 && poppedSize == 0)
return true;
int *stack = (int *)malloc(sizeof(int)*pushedSize);
int top = -1, index = 0;
for(int i = 0; i < pushedSize; i++){
stack[++top] = pushed[i];
while((top != -1) && (stack[top] == popped[index])){
top --;
index++;
}
}
if(top == -1){
return true;
}
return false;
}
17 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
思路:
首先我们要知道前序遍历和中序遍历的遍历顺序,前序遍历是 [ 根节点 | 左子树 | 右子树 ] 顺序,中序遍历是按照 [ 左子树 | 根节点 | 右子树 ] 顺序。所以前序遍历的第一个元素即为根节点,我们在中序遍历中找到该根节点,那么这个序列中的根节点左边的元素即为左子树,右边的元素为右子树。在对左右子树做相同的操作。
struct TreeNode* buildTree(int* preorder, int preorderSize, int* inorder, int inorderSize){
if(preorder == NULL || preorderSize == 0 || inorder == NULL || inorderSize == 0){
return NULL;
}
int i = 0;
struct TreeNode *root = (struct TreeNode *)malloc(sizeof(struct TreeNode));
root->val = preorder[0];
for(i = 0; i < inorderSize; i++){
if(inorder[i] == preorder[0]){
break;
}
}
root->left = buildTree(&preorder[1], i, &inorder[0], i);
root->right = buildTree(&preorder[i+1], preorderSize-i-1, &inorder[i+1], preorderSize-i-1);
return root;
}
18 回文数字
class Solution:
def isPalindrome(self , x: int) -> bool:
if x < 0: return False
x = str(x)
i, j = 0, len(x)-1
while i < j:
if(x[i] == x[j]):
i+=1
j-=1
else:
return False
return True
class Solution:
def isPalindrome(self , x: int) -> bool:
if x < 0: return False
x = str(x)
y = x[::-1]
if x==y:
return True
else:
return False
19 移动0(双指针,数组删除和添加)
数组中的0移动到最后且不改变原来的顺序
class Solution:
def moveZeroes(self , nums: List[int]) -> List[int]:
res = []
count = 0
for num in nums:
if num == 0:
count+=1
else:
res.append(num)
for i in range(count):
res.append(0)
return res
class Solution:
def moveZeroes(self , nums: List[int]) -> List[int]:
n = 0
while n < len(nums):
if len(set(nums[n:]))==1 and nums[n] == 0: return nums
if nums[n] != 0:
n+=1
continue
if nums[n] == 0:
del nums[n]
nums.append(0)
return nums
# set() 函数创建一个无序不重复元素集
在原数组的基础上操作,创建两个指针同时指向数组首,右指针向右搜索,遇到非零的交换,左指针向右移动,如果右指针遇到零继续向右移动
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
left = right = 0
while right < len(nums):
if nums[right] != 0:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right += 1
return nums
20 找出数组中消失的数字
给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。
class Solution:
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
num = set(nums)
res = []
for i in range(1, len(nums)+1):
if i not in num:
res.append(i)
return res
class Solution:
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
return list(set(range(1, len(nums) + 1))-(set(nums)))
21 盛最多水的容器(双指针)
int maxArea(int* height, int heightSize){
int i = 0;
int res = 0;
int s;
int j = heightSize -1;
while(i < j){
s = fmin(height[i],height[j]) * (j-i);
res = fmax(s, res);
if(height[i] < height[j]){
i++;
}else{
j--;
}
}
return res;
}
class Solution:
def maxArea(self, height: List[int]) -> int:
l, r = 0, len(height) - 1
ans = 0
while l < r:
area = min(height[l], height[r]) * (r - l)
ans = max(ans, area)
if height[l] <= height[r]:
l += 1
else:
r -= 1
return ans
22 爬楼梯(动态规划)
n阶楼梯,一次可以爬1/2层,爬到n层有多少种方案
class Solution:
def climbStairs(self, n: int) -> int:
if n <= 2:
return n
dp = [0] * n
dp[0] = 1
dp[1] = 2
for i in range(n-2):
dp[i+2] = dp[i+1] + dp [i]
return dp[n-1]
int climbStairs(int n){
if(n <= 2)
return n;
int *dp = (int *)malloc(sizeof(int)*n);
int i;
dp[0] =1;dp[1] = 2;
for(i=0; i < n-2; i++){
dp[i+2] = dp[i+1] + dp[i];
}
return dp[n-1];
}
23 斐波拉契数列(动态规划)
class Solution:
def fib(self, n: int) -> int:
dp = [0, 1]
for i in range(2, n + 1):
dp.append(dp[i - 1] + dp[i - 2])
return dp[n] % 1000000007
int bk[101];
int dp(n)
{
if (n <= 1)
return n;
if (bk[n])
return bk[n];
bk[n] = (dp(n - 1) + dp(n - 2)) % 1000000007;
return bk[n];
}
int fib(int n){
memset(bk, 0, sizeof(bk));
return dp(n);
}
24 统计二进制1的个数 { x = x & (x-1) }
给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。
要点: 使用 x&(x-1) 操作可以使1变为0 统计操作的次数
class Solution:
def countBits(self, n: int) -> List[int]:
def operation(x):
cnt = 0
while x > 0:
x = x & (x-1)
cnt += 1
return cnt
return [operation(i) for i in range(n+1)]
class Solution:
def countBits(self, n: int) -> List[int]:
def operation(x):
cnt = 0
while x > 0:
cnt += x & 1
x = x >> 1
return cnt
return [operation(i) for i in range(n+1)]
汉明距离(二进制对应位不同的个数)
class Solution:
def hammingDistance(self, x: int, y: int) -> int:
def operation(x):
cnt = 0
while x > 0:
cnt += x & 1
x = x >> 1
return cnt
return operation(x^y)
class Solution:
def hammingDistance(self, x, y):
return bin(x ^ y).count('1')
class Solution:
def hammingDistance(self, x, y):
ret = 0
bx, by = bin(x)[2:].zfill(32), bin(y)[2:].zfill(32)
for i in range(32):
if bx[i] != by[i]:
ret += 1
return ret
25 矩阵中的路径 (dfs)(***)
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
def dfs(i, j, k):
if not 0 <= i < len(board) or not 0 <= j < len(board[0]) or board[i][j] != word[k]: return False
if k == len(word) - 1: return True
board[i][j] = ''
res = dfs(i + 1, j, k + 1) or dfs(i - 1, j, k + 1) or dfs(i, j + 1, k + 1) or dfs(i, j - 1, k + 1)
board[i][j] = word[k]
return res
for i in range(len(board)):
for j in range(len(board[0])):
if dfs(i, j, 0): return True
return False
26 机器人的运动范围
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
def dfs(i, j, si, sj):
if i >= m or j >= n or k < si + sj or (i, j) in visited: return 0
visited.add((i,j))
return 1 + dfs(i + 1, j, si + 1 if (i + 1) % 10 else si - 8, sj) + dfs(i, j + 1, si, sj + 1 if (j + 1) % 10 else sj - 8)
visited = set()
return dfs(0, 0, 0, 0)
class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
def dfs(i, j):
if i >= m or j >= n or (i, j) in visited or i // 10 + i % 10 + j // 10 + j % 10 > k:
return 0
visited.add((i, j))
return 1 + dfs(i+1, j) + dfs(i, j+1)
visited = set()
res = dfs(0, 0)
return res
27 割绳子(动态规划+贪心算法+考虑大数)
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?
1、用一个dp数组记录从0到n长度的绳子剪掉后的最大乘积,也就是dp[i]表示长度为i的绳子剪成m段后的最大乘积
2、先把绳子剪掉第一段(长度为j),如果只剪掉长度为1,对最后的乘积无任何增益,所以从长度为2开始剪,剪了第一段后,剩下(i - j)长度可以剪也可以不剪。如果不剪的话长度乘积即为j * (i - j);如果剪的话长度乘积即为j * dp[i - j]。
class Solution:
def cuttingRope(self, n: int) -> int:
dp = [0 for _ in range(n+1)]
for i in range(2, n+1):
now = 0
for j in range(1, i):
now = max(now, max(j*(i-j), j*dp[i-j]))
dp[i] = now
return dp[n]
贪心算法:
核心思路是:尽可能把绳子分成长度为3的小段,这样乘积最大(证明略)
如果 n = 2,返回1
如果 n = 3,返回2,两个可以合并成n小于4的时候返回n - 1
如果 n = 4,返回4
如果 n > 4,分成尽可能多的长度为3的小段,每次循环长度n减去3,乘积res乘以3;最后返回时乘以小于等于4的最后一小段
以上2和3可以合并
class Solution:
def cuttingRope(self, n: int) -> int:
if n < 4:
return n - 1
res = 1
while n > 4:
res *=3
n -= 3
return res * n
考虑大数时
class Solution:
def cuttingRope(self, n: int) -> int:
if n < 4:
return n - 1
res = 1
while n > 4:
res = res * 3 % 1000000007
n -= 3
return res * n % 1000000007
28 实现幂运算(递归)
思想:按平方递归
class Solution:
def myPow(self, x: float, n: int) -> float:
def pow(x, n):
if n == 1:
return x
num = pow(x, n//2)
if n % 2 == 0:
return num * num
else:
return num * num * x
if n == 0:
return 1
if n < 0:
return 1/pow(x, -n)
return pow(x, n)
位运算 n>>1 相当于除以2 当 n&1 为真也就是说 n 为奇数时多乘一个 x
class Solution:
def myPow(self, x: float, n: int) -> float:
if x == 0: return 0
res = 1
if n < 0: x, n = 1 / x, -n
while n:
if n & 1: res *= x
x *= x
n >>= 1
return res
double myPow(double x, int n)
{
if (n == 0)
return 1; // 递归出口
if (n == 1)
return x; // 递归出口
// if (n < 0)
// return 1 / myPow(x, -n); // 此处n会越界
if (n == -1)
return 1 / x; // 只定义递归出口即可
if (n % 2 != 0)
return x * myPow(x, n - 1);
else
return myPow(x * x, n / 2);
}
29 圆圈中最后剩下的数字
0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
# 会超时,但是思想很简单
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
nums, size, start = [i for i in range(n)], n, 0
while size != 1:
i = (m + start - 1) % size
nums.pop(i)
start = i
size -= 1
return nums.pop()
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
ans = 0
for size in range(2, n + 1):
ans = (ans + m) % size
return ans
int lastRemaining(int n, int m){
int p = 0;
int i;
for(i = 2; i <= n ; i++){
p = (p+m)%i;
}
return p;
}
30 和为s的连续正数序列(滑动窗口)
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
res = []
for i in range(1, target):
sum = i
for j in range(i+1, target):
sum = sum + j
if sum == target:
res.append([k for k in range(i , j+1)])
elif sum > target:
break
return res
滑动窗口的思想(***)
先固定左右窗口为同一起点,左右窗口均只向右移动
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
i , j , sum = 1 , 1 , 0
res = []
while i <= target // 2:
if sum < target:
sum = sum + j
j += 1
elif sum > target:
sum -= i
i += 1
else:
res.append([k for k in range(i, j)])
sum -= i
i += 1
return res
int** findContinuousSequence(int target, int* returnSize, int** returnColumnSizes){
int mid=target/2;//中间值
int **res=malloc(sizeof(int*)*mid);
int *col=malloc(sizeof(int)*mid);
int x=1,y=2;
int sum=x+y;
int m=0;//m是下标
while(x<=mid)
{
if(sum<target)
sum += ++y;
else if(sum>target)
sum -= x++;
else
{
col[m]=y-x+1;//得到元素个数
res[m]=malloc(sizeof(int)*col[m]);//申请对应大小的内存
for(int i=0;i<col[m];i++)
{
res[m][i]=i+x;
}
m++;
sum -= x++;
}
}
*returnSize=m;
*returnColumnSizes=col;//col数组中存储的其实是每一个序列的元素个数
return res;
}
31 滑动窗口的最大值(滑动窗口 队列)
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
# 耗时较大
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
res = []
if len(nums) < 1:
return res
for i in range(0, len(nums)-k+1):
res.append(max(nums[i:(i+k)]))
return res
# 队列的思想 没咋看懂先摆着吧
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
deque = collections.deque()
res, n = [], len(nums)
for i, j in zip(range(1 - k, n + 1 - k), range(n)):
# 删除 deque 中对应的 nums[i-1]
if i > 0 and deque[0] == nums[i - 1]:
deque.popleft()
# 保持 deque 递减
while deque and deque[-1] < nums[j]:
deque.pop()
deque.append(nums[j])
# 记录窗口最大值
if i >= 0:
res.append(deque[0])
return res
32 丑数
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
# 暴力查找法 超时
class Solution:
def nthUglyNumber(self, n: int) -> int:
#ugly number: can be only devided by 2,3,5
def isUglyNum(num):
while num%2==0:
num //= 2
while num%3==0:
num //= 3
while num%5==0:
num //= 5
return True if num == 1 else False
cnt = 0
num = 0
while cnt<n:
num += 1
if isUglyNum(num): cnt += 1
return num
# 堆的思想
class Solution:
import heapq
def nthUglyNumber(self, n: int) -> int:
#ugly number: can be only devided by 2,3,5
#using the min-heap
res = []
ls = [1]
heapq.heapify(ls)
for _ in range(n):
cur = heapq.heappop(ls)
res.append(cur)
cur1, cur2, cur3 = cur*2, cur*3, cur*5
setls = set(ls)
setTmp = {cur1,cur2,cur3}
diff = setTmp.difference(setls)
print(diff)
if diff:
for item in diff:
heapq.heappush(ls,item)
return res[-1]
class Solution:
def nthUglyNumber(self, n):
hp = [1]
heapq.heapify(hp)
ans = 1
while n:
ans = hp[0]
heapq.heappop(hp)
if (ans % 5 == 0):
heapq.heappush(hp, ans * 5)
elif ans % 3 == 0:
heapq.heappush(hp, ans * 5)
heapq.heappush(hp, ans * 3)
else:
heapq.heappush(hp, ans * 5)
heapq.heappush(hp, ans * 3)
heapq.heappush(hp, ans * 2)
n -= 1
return ans
class Solution:
def nthUglyNumber(self, n: int) -> int:
#小根堆法
primes = [2, 3, 5]
seen = {1}
heap = [1]
for _ in range(1 , n):
cur = heapq.heappop(heap)
for prime in primes:
nxt = cur * prime
if nxt not in seen:
seen.add(nxt)
heapq.heappush(heap , nxt)
return heapq.heappop(heap)
# 动态规划
class Solution:
def nthUglyNumber(self, n: int) -> int:
#ugly number: can be only devided by 2,3,5
#DP:dp[i]:the ith ugly number
#dp[i]=min(dp[a]*2,dp[b]*3,dp[c]*5)
dp = [1 for _ in range(n)]
a = b = c = 0
for i in range(1,n):
dp[i] = min(dp[a]*2, dp[b]*3, dp[c]*5)
if dp[i] == dp[a]*2: a += 1
if dp[i] == dp[b]*3: b += 1
if dp[i] == dp[c]*5: c += 1
return dp[-1]
33 n个骰子的点数
把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
class Solution:
def dicesProbability(self, n: int) -> List[float]:
dp = [1/6 for _ in range(6)]
for i in range(2, n+1):
tmp = [0] *(5*i+1)
for j in range(len(dp)):
for k in range(6):
tmp[j+k] += dp[j] / 6
dp = tmp
return dp
34 数字序列中的某一位数字
数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。
比如输入的 n 是 365:
经过第一步计算我们可以得到第 365 个数字表示的数是三位数,n=365-9-90*2=176 digtis = 3。这时 n=176 表示目标数字是三位数中的第 176个数字。
我们设目标数字所在的数为 number,计算得到 number=100+176/3=158,idx 是目标数字在 number 中的索引,如果 idx = 0,表示目标数字是 number 中的最后一个数字。
根据步骤2,我们可以计算得到 idx = n % digits = 176 % 3 = 2,说明目标数字应该是 number = 158 中的第二个数字,即输出为 5。
class Solution:
def findNthDigit(self, n: int) -> int:
# 首先判断target是几位数,用digits表示
base = 9
digits = 1
while n - base * digits > 0:
n -= base * digits
base *= 10
digits += 1
# 计算target的值
idx = n % digits # 注意由于上面的计算,n现在表示digits位数的第n个数字
if idx == 0:
idx = digits
number = 1
for i in range(1,digits):
number *= 10
if idx == digits:
number += n // digits - 1
else:
number += n // digits
# 找到target中对应的数字
return int(str(num)[index-1])
35 不用加减乘除算加法
加法可以化简为带进位的异或运算
由于python对于负数的存储不方便
class Solution:
def add(self, a: int, b: int) -> int:
x = 0xffffffff
a, b = a&x, b&x
while b != 0:
a, b = (a ^ b), (a & b) << 1 & x
return a if a <= 0x7fffffff else ~(x^a)
36 包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
核心思想:构建一个辅助栈minstack,push的时候minstack中push的是栈顶元素和待压入元素的娇小的那个,这样就能保证minstack中的元素是降序的,调用min的时候将minstack栈顶元素弹出即可。
class MinStack:
def __init__(self):
self.stack = []
self.min_stack = [math.inf]
def push(self, x: int) -> None:
self.stack.append(x)
self.min_stack.append(min(x, self.min_stack[-1]))
def pop(self) -> None:
self.stack.pop()
self.min_stack.pop()
def top(self) -> int:
return self.stack[-1]
def min(self) -> int:
return self.min_stack[-1]
37 栈与队列的互相实现
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
class CQueue:
def __init__(self):
self.A, self.B = [], []
def appendTail(self, value: int) -> None:
self.A.append(value)
def deleteHead(self) -> int:
if self.B: return self.B.pop()
if not self.A: return -1
while self.A:
self.B.append(self.A.pop())
return self.B.pop()
class MyQueue:
def __init__(self):
self.A, self.B = [], []
def push(self, x: int) -> None:
self.A.append(x)
def pop(self) -> int:
if self.B:
return self.B.pop()
if not self.A:
return None
while self.A:
self.B.append(self.A.pop())
return self.B.pop()
def peek(self) -> int:
ans = self.pop()
self.B.append(ans)
return ans
def empty(self) -> bool:
return not self.A and not self.B
用两个队列实现栈
有一个队列一直是空的
class MyStack:
def __init__(self):
self.Deque_in = collections.deque()
self.Deque_out = collections.deque()
def push(self, x: int) -> None:
self.Deque_in.append(x)
while self.Deque_out:
self.Deque_in.append(self.Deque_out.popleft())
self.Deque_in, self.Deque_out = self.Deque_out, self.Deque_in
def pop(self) -> int:
return self.Deque_out.popleft()
def top(self) -> int:
return self.Deque_out[0]
def empty(self) -> bool:
return not self.Deque_out
用一个队列实现栈
class MyStack:
def __init__(self):
self.Deque = collections.deque()
def push(self, x: int) -> None:
self.Deque.append(x)
for i in range(len(self.Deque)):
self.Deque.append(self.Deque.popleft())
def pop(self) -> int:
return self.Deque.pop()
def top(self) -> int:
return self.Deque[-1]
def empty(self) -> bool:
return not self.Deque
38 不允许使用*/%求商
class Solution:
def divide(self, a: int, b: int) -> int:
ret = 0
flag = True if (a > 0 and b > 0) or (a < 0 and b < 0) else False
a, b = abs(a), abs(b)
def calc(x, y):
n = 1
while x > y << 1:
y <<= 1
n <<= 1
return n, y
while a >= b:
cnt, val = calc(a, b)
ret += cnt
a -= val
ret = -ret if flag else ret
return ret - 1 if ret >= 2 ** 31 else ret
39 二进制加法
给定两个 01 字符串 a 和 b ,请计算它们的和,并以二进制字符串的形式输出。
模拟法:
class Solution:
def addBinary(self, a: str, b: str) -> str:
i, j = len(a)-1, len(b)-1
a = list(map(int, a))
b = list(map(int, b))
t, acc = 0, 0
res = []
while i>=0 or j >=0:
x = a[i] if i>=0 else 0
y = b[j] if j>=0 else 0
t = (x + y + acc) % 2
acc = (x + y + acc) // 2
res.append(str(t))
i -= 1
j -= 1
if acc > 0:
res.append(str(acc))
return(''.join(res[::-1]))
二进制位运算,不使用+:
class Solution:
def addBinary(self, a: str, b: str) -> str:
a, b = int(a, 2), int(b, 2)
while b != 0:
a, b = a^b, (a&b)<<1
return bin(a)[2:]
40 三数之和等于0
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
ans = []
nums.sort()
for i in range(n - 2):
if i > 0 and nums[i] == nums[i - 1]:
continue
ll, rr = i + 1, n - 1
target = - nums[i]
while ll < rr:
sum_ = nums[ll] + nums[rr]
if sum_ == target:
ans.append([nums[i], nums[ll], nums[rr]])
while ll < rr:
ll += 1
if nums[ll] != nums[ll - 1]:
break
while ll < rr:
rr -= 1
if nums[rr] != nums[rr + 1]:
break
elif sum_ > target:
rr -= 1
else:
ll += 1
return ans
41 和为k的子数组
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
presum_map = defaultdict(int)
presum_map[0] = 1
presum, ans = 0, 0
for i in range(len(nums)):
presum += nums[i]
target = presum - k
if target in presum_map:
ans += presum_map[target]
presum_map[presum] += 1
return ans
42 有效的括号
class Solution:
def isValid(self, s: str) -> bool:
stack = []
for x in s:
if x == '(':
stack.append(')')
elif x == '{':
stack.append('}')
elif x == '[':
stack.append(']')
elif not stack or stack[-1]!=x:
return False
else:
stack.pop()
return True if not stack else False
#使用字典匹配
class Solution:
def isValid(self, s: str) -> bool:
stack = []
dic = {
'(': ')',
'{': '}',
'[': ']'
}
for x in s:
if x in dic.keys():
stack.append(dic[x])
elif not stack or stack[-1] != x:
return False
else:
stack.pop()
return True if not stack else False
43 三数之和
直接上代码
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
nums.sort()
ans = []
# first、second、third三个指针,三个循环,先固定first,second从first+1开始,third从最后开始往前
for first in range(n):
# 遍历到相同的元素跳过
if first > 0 and nums[first] == nums[first-1]: continue
third = n - 1
target = -nums[first]
for second in range(first+1, n):
if second > first+1 and nums[second] == nums[second-1]: continue
# 需要保证 b 的指针在 c 的指针的左侧
while second < third and nums[second] + nums[third] > target:
third -= 1
if second == third: break
if nums[second] + nums[third] == target:
ans.append([nums[first], nums[second], nums[third]])
return ans
44 四数之和
四数之和与三数之和的思想基本一样,先固定N-2个指针,然后移动双指针,注意一些提前结束循环的判断条件以及相同元素的去重
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
res = []
n = len(nums)
if not nums or n < 4:
return res
nums.sort()
for i in range(n-3):
if i > 0 and nums[i] == nums[i-1]:
continue
if nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target:
break
if nums[i] + nums[n-3] + nums[n-2] + nums[n-1] < target:
continue
for j in range(i+1, n-2):
if j > i+1 and nums[j] == nums[j-1]:
continue
if nums[i] + nums[j] + nums[j+1] + nums[j+2] > target:
break
if nums[i] + nums[j] + nums[n-2] + nums[n-1] < target:
continue
left, right = j+1, n-1
while left < right:
total = nums[i] + nums[j] + nums[left] + nums[right]
if total == target:
res.append([nums[i], nums[j], nums[left], nums[right]])
while left < right and nums[left] == nums[left+1]:
left += 1
left += 1
while left < right and nums[right] == nums[right-1]:
right -= 1
right -= 1
elif total < target:
left += 1
else:
right -= 1
return res