C
数组
查找
二维数组查找 J04
在查找数组里是否有这个整数时,我们可以按行和列去查找。选取的是右上角的数去和target比较。
a)如果array[i][j] > target 则向左走 即 j–;
b) 如果array[i][j] < target 则向下走 即 i++。
bool findNumberIn2DArray(int** matrix, int matrixSize, int* matrixColSize, int target){
if((matrixSize == 0)||(*matrixColSize == 0))
return false;
int i=0;
int j=*matrixColSize - 1;
while((i<matrixSize)&&(j>=0))
{
if(matrix[i][j] == target)
return true;
else if(matrix[i][j] > target)
j--;
else if(matrix[i][j] < target)
i++;
}
return false;
}
旋转数组的最小数字 J11
实例:
输入:[3,4,5,1,2]
输出:1
输入:[2,2,2,0,1]
输出:0
直接查找也行,或者二分查找。
int minArray(int* numbers, int numbersSize){
int min;
min = numbers[0];
for(int i = 1;i<numbersSize;i++)
{
if(min > numbers[i])
{
min = numbers[i];
}
}
return min;
}
int minArray(int* numbers, int numbersSize) //二分查找(结合题意)
{
int i = 0;
int j = numbersSize-1;
while(i<j)
{
int mid = (i+j)/2;
if(numbers[mid] > numbers[j])
i = mid+1;
else if(numbers[mid] <= numbers[j])
j=j-1;
}
return numbers[i];
}
边界
顺时针打印矩阵 J29
分析:
打印矩阵的顺序是 “从左向右、从上向下、从右向左、从下向上” 循环。
代码:
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix)
{
if(matrix.empty()) return {}; //容器成员为数组,NULL会报错
vector<int> res;
int l = 0;
int r = matrix[0].size() - 1;
int t = 0;
int b = matrix.size() - 1;
while(true)
{
//left-->right
for(int i=l;i<=r;i++)
{
res.push_back(matrix[t][i]);
}
if(++t > b) break; //四个判断设计到打印截止条件!
//top-->bottom
for(int i=t;i<=b;i++)
{
res.push_back(matrix[i][r]);
}
if(--r < l) break;
//right-->left
for(int i=r;i>=l;i--)
{
res.push_back(matrix[b][i]);
}
if(--b < t) break;
//bottom-->top
for(int i=b;i>=t;i--)
{
res.push_back(matrix[i][l]);
}
if(++l > r) break;
}
return res;
}
};
字符串
替换
把字符串 s 中的每个空格替换成"%20" 。J05
char* replaceSpace(char* s){
char *str = (char*)malloc(sizeof(char)*10000);
int i=0;
while(*s!='\0')
{
if(*s == ' ')
{ str[i++] = '%';
//用单引号引起的一个字符大小就是一个字节。而用双引号引起的字符串大小是字符的总大小+1,因为用双引号引起的字符串会在字符串末尾添加一个二进制为0的字符'\0'。
str[i++] = '2';
str[i++] = '0';
}
else
{
str[i++] = *s;
}
s++;
}
str[i] = '\0';
return str;
}
链表
逆置
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。J06
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* reversePrint(struct ListNode* head, int* returnSize){
if(head == NULL)
{
*returnSize=0;
return 0;
}
/* 有空头结点的情况 //天勤数据结构2020P49
struct ListNode *p = head;
struct ListNode *q ;
head->next = NULL;
int len =1; //如果是0输出有问题(有头)。
while(p!=NULL)
{
len++;
q = p->next;
p->next = head->next;
head->next = p;
p = q;
}
*/
//无空头结点情况
struct ListNode *l = head;
struct ListNode *q;
int len=1;
while(head->next != NULL)
{
len++;
q = head->next;
head->next = q->next;
q->next = l;
l = q;
}
*returnSize = len;
int *res = (int*)malloc(len*sizeof(int));
int i;
for(i=0;i<len;i++)
{
res[i] = l->val;
l = l->next;
}
return res;
}
反转链表J24
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
if(head==NULL) return; //这步漏了会一直执行正常但提交报错
struct ListNode *p = head->next,*q;
head->next=NULL;
while(p) //同天勤2020 P49第八题
{
q=p->next;
p->next=head;
head=p;
p=q;
}
return head;
}
删除节点
示例:输入: head = [4,5,1,9], val = 5 输出: [4,1,9](J18)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteNode(struct ListNode* head, int val){
if(head==NULL)
return NULL;
if(head->val==val)
return head->next;
struct ListNode* p = head;
while(p->next!=NULL)
{
if(p->next->val==val)
{
p->next = p->next->next;
return head;
}
p = p->next;
}
if(p->val==val) //最后一个情况
{
p=NULL;
}
return head;
}
别人做法
http://wordaligned.org/articles/two-star-programming
https://leetcode-cn.com/problems/shan-chu-lian-biao-de-jie-dian-lcof/solution/linusda-lao-de-jie-jue-fang-an-by-adjwang/https://www.cnblogs.com/qwertWZ/archive/2013/02/10/2909699.html
struct ListNode* deleteNode(struct ListNode* head, int val){
struct ListNode** indirect = &head;
//struct ListNode* temp;
for(; *indirect; indirect = &((*indirect)->next)){
if((*indirect)->val == val){
//temp = *indirect;
*indirect = (*indirect)->next;
//free(temp);
break;
}
}
return head;
}
①一般链表只有在会修改头结点的函数,才会传struct node * *;
②使用struct node **的目的是,这个函数内部需要改变这个指针参数,并且要作用到调用者上
- head_ref = prev;
③使用struct node *,只是函数内需要使用这个指针,无需改变它。
输出倒数第k个结点
示例:给定一个链表: 1->2->3->4->5, 和 k = 2.返回链表 4->5. J22
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* getKthFromEnd(struct ListNode* head, int k){
int n=0;
struct ListNode* p = head;
while(p)
{
p = p->next;
n++;
}
for(int i=0;i<n-k;i++)
head = head->next;
return head;
}
双指针方法(常规)也可。
https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/solution/cyu-yan-chang-gui-zuo-fa-by-bevischou-63/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* getKthFromEnd(struct ListNode* head, int k){
struct ListNode*slow_p=head,*p=head;
while(k--) p=p->next;
while(p!=0){
slow_p=slow_p->next;
p=p->next;
}
return slow_p;
}
合并两个排序的链表
示例:输入:1->2->4, 1->3->4;输出:1->1->2->3->4->4。 J25
①递归思想
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
if(l1==NULL)
return l2;
if(l2==NULL)
return l1;
struct ListNode *p=NULL;
if(l1->val<l2->val)
{
p = l1;
p->next = mergeTwoLists(l1->next,l2);
}
else
{
p = l2;
p->next = mergeTwoLists(l1,l2->next);
}
return p;
}
②迭代思想
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
struct ListNode *node1=l1;
struct ListNode *node2=l2;
int temp;
if(!l1) return l2;
while(node1&&node2)
{
if(node1->val<=node2->val)
{
if(node1->next==NULL) break;
node1=node1->next;
}
else
{
l2=l2->next;
node2->next=node1->next;
node1->next=node2;
temp=node2->val; //画个图分析知道此事node2>node1
node2->val=node1->val;
node1->val=temp;
node1=node2;
node2=l2;
}
}
if(node1->next==NULL&&l2!=NULL) node1->next=node2;
return l1;
}
哈希表
找重复
找出一个由0~n-1数字构成的数组中重复的数字。J03
int findRepeatNumber(int* nums, int numsSize){
char hash[numsSize];
memset(hash, 0, numsSize);
//memset 复制字符 c(0)(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符
for(int i=0;i<numsSize;i++)
{
if(hash[nums[i]])
return nums[i];
hash[nums[i]] = 1;
}
return -1;
}
或者
int findRepeatNumber(int* nums, int numsSize){
int *hash = (int*)calloc(numsSize,sizeof(int));
//表示定义bai一个int类型的指针变量a,并申请n*sizeof(int)个字节(即4*n个字节)的存储空间。
for(int i=0;i<numsSize;i++)
{
if(hash[nums[i]])
return nums[i];
hash[nums[i]] = 1;
}
return -1;
}
递归
二叉树
重建
由前序,中序遍历还原出二叉树。J07
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
struct TreeNode* buildTree(int* preorder, int preorderSize, int* inorder, int inorderSize){
if(preorderSize == 0)
{
return NULL;
}
struct TreeNode* Tree = calloc(1,sizeof(struct TreeNode));
Tree -> val = preorder[0];
int *left1 = calloc(preorderSize,sizeof(int));
int *right1 = calloc(preorderSize,sizeof(int));
int *left2 = calloc(inorderSize,sizeof(int));
int *right2 = calloc(inorderSize,sizeof(int));
int i,j;
for(i=0;inorder[i]!=Tree->val;i++)
{
left2[i] = inorder[i];
}
for(j=0;j<i;j++)
{
left1[j] = preorder[j+1]; //先序记得+1,否则出错
}
int u,v;
int k = 0;
for(u=i+1;u<inorderSize;u++)
{
right2[k++] = inorder[u];
}
k=0;
for(v=j+1;v<preorderSize;v++)
{
right1[k++] = preorder[v];
}
Tree->left = buildTree(left1,j,left2,j); //分别对左右子树递归运算
Tree->right = buildTree(right1,k,right2,k);
return Tree;//记得返回
}
1.两个函数:
malloc(size_t size);
calloc(size_t numElements,size_t sizeOfElement)。
①初试话内存差别:
malloc函数:不能初始化所分配的内存空间,在动态分配完内存后,里边数据是随机的垃圾数据。
calloc函数:能初始化所分配的内存空间,在动态分配完内存后,自动初始化该内存空间为零。
②返回值差别:
malloc函数:函数返回值是一个对象。
calloc函数:函数返回值是一个数组。
2.数组用下标取值其实是先转化为指针的——a[i]先转化为*(a+i);
3.解题前将图画出来分析。
另解:
结合前序和中序的特点,加入变量index作为子树长度,减少内存。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
struct TreeNode* buildTree(int* preorder, int preorderSize, int* inorder, int inorderSize){
if(preorderSize == 0)
{
return NULL;
}
struct TreeNode* Tree = calloc(1,sizeof(struct TreeNode));
Tree -> val = preorder[0];
int index = 0;
while((index < preorderSize)&&(inorder[index]!=preorder[0]))
{
index++;
}
Tree->left = buildTree(preorder+1,index,inorder,index);
Tree->right = buildTree(preorder+1+index,preorderSize-1-index,inorder+1+index,preorderSize-1-index);
return Tree;
}
镜像
示例:输入:root = [4,2,7,1,3,6,9];输出:[4,7,2,9,6,3,1] J27
①值
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
struct TreeNode* mirrorTree(struct TreeNode* root){
if(root==NULL)
{
return NULL;
}
struct TreeNode* p = (struct TreeNode*)malloc(sizeof(struct TreeNode));
p->val = root->val;
p->right = mirrorTree(root->left);
p->left = mirrorTree(root->right);
return p;
}
法②(指针)递归
https://leetcode-cn.com/problems/er-cha-shu-de-jing-xiang-lcof/solution/mian-shi-ti-27-er-cha-shu-de-jing-xiang-di-gui-fu-/
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
struct TreeNode* mirrorTree(struct TreeNode* root){
if(!root)
return NULL;
struct TreeNode *tmp = root->left;
root->left = mirrorTree(root->right);
root->right = mirrorTree(tmp);
return root;
}
更便于理解如下:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
struct TreeNode* mirrorTree(struct TreeNode* root){
if (root == NULL) {
return NULL;
}
struct TreeNode *leftRoot = mirrorTree(root->right);
struct TreeNode *rightRoot = mirrorTree(root->left);
root->left = leftRoot;
root->right = rightRoot;
return root;
}
迭代/递归
斐波那契数列J10.1
(数组(记忆化递归)解决)
int fib(int n){
int i;
int num[103]={0} ;
num[0] = 0;
num[1] = 1;
for(i=2;i<=n;i++)
{
num[i] = (num[i-2] + num[i-1]) % 1000000007;
}
return num[n];
}
可以使用动态规划方法
int fib(int n){
if(n<2)
return n;
int a = 0;
int b = 1;
int sum;
while(n-1)
{
sum = (a+b)%1000000007;
a = b;
b = sum;
n--;
}
return sum;
}
可以使用递归方法
int fib(int n){
int temp;
static int num[103]={0} ;
num[0] = 0;
num[1] = 1;
if(num[n]!=0)
{
return num[n];
}
if(n>1)
{
temp = fib(n-1) + fib(n-2);
num[n] = temp % 1000000007;
}
return num[n];
}
static int a是静态的整型变量.从定义开始到程序结束都存在。如果定义在函数里,则每次调用该函数时该变量都存在,并且它的值都保存下来。
例如:
void add(){
static int a = 0;
printf("%d",a++);
}
void main(){
for(int i=0;i<10;i++)
add();
}
每次打印出来的都不一样,从0一直到9
青蛙跳台阶。J10.2
青蛙跳台阶问题: f(0)=1f(0)=1 , f(1)=1f(1)=1 , f(2)=2f(2)=2 ;
斐波那契数列问题: f(0)=0f(0)=0 , f(1)=1f(1)=1 , f(2)=1f(2)=1 。
int numWays(int n){
int f0=1;
int f1=1;
int sum,i;
if(n<2)
return 1;
for(i=2;i<=n;i++)
{
sum = (f0+f1)%1000000007;
f0 = f1;
f1 = sum;
}
return f1;
}
数值整数次方 J16
示例 :
输入: 2.00000, 10
输出: 1024.00000
-100.0 < x < 100.0
n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。
分析
①n=0 直接返回1
②n<0 需要转为整数,且x转为-x
③n>0 讨论奇偶数情况。偶数Power(xx,n/2) 奇数xPower(x*x,n/2)
递归法
class Solution {
public:
double myPow(double x, int n) {
long exp = n;
if(exp==0)
return 1;
if(exp<0)
return 1/x*myPow(1/x,-exp-1); //因为exp会溢出,为了避免需要提一次到前面,而后-1
return (exp%2==0)?myPow(x*x,exp/2):x*myPow(x*x,exp/2);
}
};
非递归法
此时无需考虑计算过程中n的正负问题,秩序最后结尾做一下判断
class Solution {
public:
double myPow(double x, int n) {
double result = 1.0;
for(int i=n;i!=0;i/=2,x*=x)
{
if(i%2!=0) //已经涵盖了两种情况
result*=x;
}
return n<0?1.0/result:result;
}
};
队列 / 栈
两个栈实现队列。J09
解题思路1(内存消耗:81.8M)
队列栈 + 辅助栈
(队头为队列栈顶,队尾为队列栈底)
效率:入队慢,出队快
typedef struct {
int len;
int top1;
int top2;
int *s1;
int *s2;
} CQueue;
CQueue* cQueueCreate() {
CQueue *queue = malloc(sizeof(CQueue));
queue->len = 10000;
queue->s1 = malloc(queue->len*sizeof(int));
queue->s2 = malloc(queue->len*sizeof(int));
queue->top1 = -1;
queue->top2 = -1;
return queue;
}
void cQueueAppendTail(CQueue* obj, int value) {
//tqP59 入出栈,是[],不是()
while(obj->top1!=-1)
{
obj->s2[++(obj->top2)] = obj->s1[obj->top1--];
}
obj->s1[++(obj->top1)] = value;
while(obj->top2!=-1)
{
obj->s1[++(obj->top1)] = obj->s2[obj->top2--];
}
}
int cQueueDeleteHead(CQueue* obj) {
if(obj->top1 == -1)
{
return -1;
}
else
{
return obj->s1[obj->top1--];
}
}
void cQueueFree(CQueue* obj) {
free(obj->s1);
free(obj->s2);
free(obj);
}
/**
* Your CQueue struct will be instantiated and called as such:
* CQueue* obj = cQueueCreate();
* cQueueAppendTail(obj, value);
* int param_2 = cQueueDeleteHead(obj);
* cQueueFree(obj);
*/
解题思路2(82M)
队列栈 + 辅助栈
(队头为队列栈底,队尾为队列栈顶)
效率:入队快,出队慢
typedef struct {
int len;
int top1;
int top2;
int *s1;//栈1,入栈=入队
int *s2;//栈2,出栈=出队
} CQueue;
CQueue* cQueueCreate() {
CQueue *queue = malloc(sizeof(CQueue));
queue->len = 10000;
queue->top1 = -1;
queue->top2 = -1;
queue->s1 = malloc(queue->len * sizeof(int));
queue->s2 = malloc(queue->len * sizeof(int));
return queue;
}
void cQueueAppendTail(CQueue* obj, int value) {
obj->s1[++(obj->top1)] = value;
}
int cQueueDeleteHead(CQueue* obj) {
if(obj->top1 == -1)
return -1;
else
{
while(obj->top1!=-1)//队列不空,把队列放入辅助栈
{
obj->s2[++(obj->top2)] = obj->s1[obj->top1--];//队尾(栈顶)依次复制到辅助栈并出栈
}
int t = obj->s2[obj->top2--];//用t暂存队头(辅助栈顶),队头(辅助栈顶)出队(出辅助栈)。
while(obj->top2!=-1)//辅助栈剩下元素放入队列
{
obj->s1[++(obj->top1)] = obj->s2[obj->top2--];//辅助栈顶复制到队尾(栈顶)并出栈
}
return t; //返回出队元素
}
}
void cQueueFree(CQueue* obj) {
free(obj->s1);
free(obj->s2);
free(obj);
}
/**
* Your CQueue struct will be instantiated and called as such:
* CQueue* obj = cQueueCreate();
* cQueueAppendTail(obj, value);
* int param_2 = cQueueDeleteHead(obj);
* cQueueFree(obj);
*/
解题思路3
出队栈 + 入队栈
(出队栈不空时:队头为出队栈顶,队尾为入队栈顶)
(出队栈空时:队头为入队栈顶,队尾为入队栈底)
效率:入队快,出队慢(但是整体比前2种都快)
typedef struct {
int len;
int top1;
int top2;
int *s1;//栈1,入栈=入队
int *s2;//栈2,出栈=出队
} CQueue;
CQueue* cQueueCreate() {
CQueue *queue = malloc(sizeof(CQueue));
queue->len = 10000;
queue->top1 = -1;
queue->top2 = -1;
queue->s1 = malloc(queue->len * sizeof(int));
queue->s2 = malloc(queue->len * sizeof(int));
return queue;
}
void cQueueAppendTail(CQueue* obj, int value) {
obj->s2[++(obj->top2)] = value;
}
int cQueueDeleteHead(CQueue* obj) {
if((obj->top1 == -1)&&(obj->top2 == -1))
return -1;
else
{
if(obj->top1 == -1)
{
while(obj->top2 != -1)
{
obj->s1[++(obj->top1)] = obj->s2[obj->top2--];
}
}
return obj->s1[obj->top1--];
}
}
void cQueueFree(CQueue* obj) {
free(obj->s1);
free(obj->s2);
free(obj);
}
/**
* Your CQueue struct will be instantiated and called as such:
* CQueue* obj = cQueueCreate();
* cQueueAppendTail(obj, value);
* int param_2 = cQueueDeleteHead(obj);
* cQueueFree(obj);
*/
辅助栈
包含min函数的栈 J30
min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
思路:
代码:
class MinStack {
public:
/** initialize your data structure here. */
int stackA[20001];
int topA = -1; //《天勤》P59
int stackB[20001];
int topB = -1;
MinStack(){
}
void push(int x)
{
stackA[++topA] = x;
if(topB == -1 || stackB[topB] >= x)
{
stackB[++topB] = x;
}
}
void pop()
{
if(stackA[topA]==stackB[topB])
{
topB--;
}
topA--;
}
int top()
{
return stackA[topA];
}
int min()
{
return stackB[topB];
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(x);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->min();
*/
使用容器更规范:
class MinStack {
public:
/** initialize your data structure here. */
stack<int> A;
stack<int> B;
MinStack(){
}
void push(int x)
{
A.push(x);
if(B.empty() || B.top() >= x) B.push(x);
}
void pop()
{
if(B.top() == A.top()) B.pop();
A.pop();
}
int top()
{
return A.top();
}
int min()
{
return B.top();
}
};
判断栈弹出的正确性 J31
例:
输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
分析:
算法流程:
初始化: 辅助栈 stackstack ,弹出序列的索引 i ;
遍历压栈序列: 各元素记为 num ;
元素 num入栈;
循环出栈:若 stack 的栈顶元素 = 弹出序列元素 popped[i] ,则执行出栈与 i++ ;
返回值: 若 stack 为空,则此弹出序列合法。
代码:
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
stack<int> temp;
int j=0;
for(int i=0;i!=pushed.size();)
{
temp.push(pushed[i]);
i++;
while(!temp.empty()&&temp.top()==popped[j])
{
temp.pop();
j++;
}
}
return temp.empty();
// if(temp.empty()) return true;
// return false;
}
};
位运算
求某一个数的二进制表示中1的个数 J15
int hammingWeight(uint32_t n) {
//32位无符号整形数,这个你得看它的头文件,一般都是有宏定义的,如#define unsigned int uint32_t
int count;
while(n != 0)
{
if(n%2 == 1)
{
count++;
}
n = n/2;
}
return count;
}
另一种方法
int hammingWeight(uint32_t n) {
int count;
while(n != 0)
{
n = n&(n-1);
count++;
}
return count;
}
n&(n-1):(位与知识)
原理:将n的二进制表示中的最低位为1的改为0,如:
n = 10100(二进制),则(n-1) = 10011 ==》n&(n-1) = 10000
可以看到原本最低位为1的那位变为0。
作用:
1.判断一个数是否是2的方幂
n > 0 && ((n & (n - 1)) == 0 )
令:n=1101011000(二进制,十进制也一样),则n-1=1101010111。n&(n-1)=1101010000
由此可以得出,n和n-1的低位不一样,直到有个转折点,就是借位的那个点,从这个点开始的高位,n和n-1都一样,如果高位一样这就造成一个问题,就是n和n-1在相同的位上可能会有同一个1,从而使((n & (n-1)) != 0),如果想要((n & (n-1)) == 0),则高位必须全为0,这样就没有相同的1。
所以n是2的幂或0
2.求某一个数的二进制表示中1的个数
本题。
3.计算N!的质因数2的个数。
N!质因数2的个数 = [N / 2] + [N / 4] + [N / 8] + …
(10101)!的质因数2的个数为10000 - 1 + 00100 - 1 + 00001 - 1 = 10101 - 3(二进制表示中1的个数)。
推及一般N!的质因数2的个数为N - (N二进制表示中1的个数)。
链接:https://blog.csdn.net/zheng0518/article/details/8882394
数学
打印从1到最大的n位数 J17
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* printNumbers(int n, int* returnSize){
int size = pow(10.0,n) - 1;
int *output = (int*)malloc(sizeof(int)*size);
for(int i=0;i<size;i++)
{
output[i] = i+1;
}
*returnSize = size;//此句为便于系统检测。
return output;
}
pow(x,y); //其作用是计算x的y次方。x、y及函数值都是double型。
动态规划
剪绳子(J14-1)
一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?
示例:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
int cuttingRope(int n)
{
if(n<=3)
{
return n-1;
}
int a=0,b=0;
while(n>=3)
{
n -= 3;
a++;
}
b = n % 3;
if(b==0)
return pow(3,a);
if(b==1)
return 4*pow(3,a-1);
if(b==2)
return 2*pow(3,a);
return 0;
}
字符串
表示数值的字符串 J20
实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、"-1E-16"、“0123"都表示数值,但"12e”、“1a3.14”、“1.2.3”、"±5"及"12e+5.4"都不是。
分析:
字符类型
空格 「 」、数字「 0—9 」 、正负号 「 ± 」 、小数点 「 . 」 、幂符号 「 eE 」 。
几种状态:(合法结束状态2378)
1. 开始的空格
2. 幂符号前的正负号
3. 小数点前的数字
4. 小数点、小数点后的数字
5. 当小数点前为空格时,小数点、小数点后的数字
6. 幂符号
7. 幂符号后的正负号
8. 幂符号后的数字
9. 结尾的空格
出现过的错误:
输入:". 1" (中间有空格)
输出:true
预期:false
输入:“0e”
输出:true
预期:false
输入:".1"
输出:false
预期:true
输入:"1 " (结尾有空格)
输出:false
预期:true
输入:“0”
输出:false
预期:true
输入:“e”
输出:true
预期:false
输入:"."
输出:true
预期:false
代码:
class Solution {
public:
bool isNumber(string s) {
bool m_dot = false,m_eorE = false,m_Op = false,m_Num = false;
int index = 0;
// if(!s.empty()) //清除字符串所有空格
// {
// while((index = s.find(' ',index)) != string::npos)
// {
// s.erase(index,1);
// }
// }
if( !s.empty() ) //清除字符串前后空格
{
s.erase(0,s.find_first_not_of(" "));
s.erase(s.find_last_not_of(" ") + 1);
}
int n = s.size();
for(int i = 0;i<n;i++)
{
if(s[i]<='9'&&s[i]>='0') m_Num = true;
else if(s[i]=='e'||s[i]=='E')
{
if(m_eorE||!m_Num) return false;
m_eorE = true;
m_Num = false;m_dot = false;m_Op = false;
}
else if(s[i] == '+'||s[i] == '-')
{
if(m_Num||m_Op||m_dot) return false;
m_Op = true;
}
else if(s[i] == '.')
{
if(m_dot||m_eorE) return false;
m_dot = true;
}
else return false;
}
return m_Num;
}
};
树
DFS+剪枝(回溯)
矩阵中的路径 J12
示例:
输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
输入:board = [[“a”,“b”],[“c”,“d”]], word = “abcd”
输出:false
bool dfs(char** board, int boardSize, int* boardColSize, char* word,int row,int col,int len)
{
if(row>=boardSize||row<0||col>=*boardColSize||col<0||board[row][col]!=word[len]) //boardColSize要*
{
return false;
}
if(len == strlen(word)-1)
{
return true;
}
char tmp = board[row][col];
board[row][col] = '/';
bool res = dfs(board,boardSize,boardColSize,word,row+1,col,len+1)||dfs(board,boardSize,boardColSize,word,row-1,col,len+1)||dfs(board,boardSize,boardColSize,word,row,col+1,len+1)||dfs(board,boardSize,boardColSize,word,row,col-1,len+1) ;//分类要覆盖完整
board[row][col] = tmp;
return res;
}
bool exist(char** board, int boardSize, int* boardColSize, char* word){
for(int i=0;i<boardSize;i++)
{
for(int j=0;j<*boardColSize;j++)
{
if(dfs(board,boardSize,boardColSize,word,i,j,0)) //len零始,对每一个位置进行判断,只要出现一次路径成功遍历的情况,则直接返回true
{
return true;
}
}
}
return false;
}
树的子结构
J26
示例:
1:输入:A = [1,2,3], B = [3,1]
输出:false
2:
输入:A = [3,4,5,1,2], B = [4,1]
输出:true
思路:https://leetcode-cn.com/problems/shu-de-zi-jie-gou-lcof/solution/mian-shi-ti-26-shu-de-zi-jie-gou-xian-xu-bian-li-p/
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool recur(struct TreeNode* root1,struct TreeNode *root2)
{
if(root2==NULL)
return true;
if(root1==NULL||root1->val!=root2->val)
return false;
return (recur(root1->left,root2->left)&&recur(root1->right,root2->right));
}
bool isSubStructure(struct TreeNode* A, struct TreeNode* B){
if(A==NULL||B==NULL)
return false;
return recur(A,B)||isSubStructure(A->left,B)||isSubStructure(A->right,B);
}
DFS/BFS
机器人的运动范围 J13
地上有一个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。请问该机器人能够到达多少个格子?
DFS:
int bitSum(int n) { //数位和的计算方法
int sum = 0;
while(n > 0) {
sum += n % 10;
n = n/10;
}
return sum;
}
int dfs(int *visited, int m, int n, int k, int i, int j) {
if(i >= m || j >= n || visited[i*n+j] || bitSum(i) + bitSum(j) > k) return 0;
visited[i*n+j] = 1;
return 1 + dfs(visited, m, n, k, i + 1, j) + dfs(visited, m, n, k, i, j + 1) ;
}
int movingCount(int m,int n,int k)
{
int visited[m * n];
memset(visited,0,m*n*sizeof(int)); //作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。
//visited[m*n]={0};
int ans = dfs((int*)visited,m,n,k,0,0);
//int *a[10] :数组指bai针。数组a里存放的是du10个int型指针
//int (*a)[10] :a是指针,指向一个数组。此数组有zhi10个int型元素
//(int*)a将指针变量a强制转换为整型指针,说明a一般不是一个整型指针,也可以是个整型指针。int*a,定义一个整型指针变量a
//这里不转也可运行。
return ans;
}
注:如果是int dfs(int visited[m][n], int m, int n, int k, int i, int j)
则会错误,[m][n]未指定长度,[1][2] (具体长度)则没错。
二维数组作为形参传递的方法:
https://www.jb51.net/article/169212.htm
int movingCount(int m, int n, int k){
int ans=0;
int queue[m*n][2];
int head=-1,tail=-1;
int visited[m][n];
memset(visited,0,m*n*sizeof(int));
tail++;
queue[tail][0]=0;
queue[tail][1]=0;
while(head!=tail)
{
head++;
int i=queue[head%(m * n)][0]; //天勤 P65 入队出队
int j=queue[head%(m * n)][1];
if(i<0||i==m||j<0||j==n||i%10+i/10+j%10+j/10>k||visited[i][j]==1)
continue;
ans++;
visited[i][j]=1;
tail++;
queue[tail% (m * n)][0]=i+1;//向右,入队
queue[tail% (m * n)][1]=j;
tail++;
queue[tail% (m * n)][0]=i;//向下,入队
queue[tail% (m * n)][1]=j+1;
}
return ans;
}
二叉树深度 J55
DFS:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
int maxDepth(struct TreeNode* root){
if(root==NULL)
return 0;
return fmax(maxDepth(root->left),maxDepth(root->right))+1;
}
BFS:(层次遍历)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
int maxDepth(struct TreeNode* root){
int deep=0;
if(root==NULL)
return 0;
struct TreeNode* queue[10003];
int head=0,tail=0;
queue[tail++]=root;
struct TreeNode* tmp;
while(head!=tail)
{
int len=tail-head;
for(int i=0;i<len;i++)
{
tmp = queue[head++];
if(tmp->left) queue[tail++]=tmp->left;
if(tmp->right) queue[tail++]=tmp->right;
}
deep++;
}
return deep;
}
对称二叉树判断 J28
(递归、回溯)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool compare(TreeNode *Left,TreeNode *Right)
{
if(Left==NULL&&Right==NULL) return true;
if(Left==NULL||Right==NULL||Left->val!=Right->val) return false;
return compare(Left->left,Right->right)&&compare(Left->right,Right->left);
}
bool isSymmetric(TreeNode* root) {
if(root==NULL) return true;
return compare(root->left,root->right);
}
};
层次遍历 J32
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
例如:
给定二叉树: [3,9,20,null,null,15,7],
返回:
[3,9,20,15,7]
代码:
/**
1. Definition for a binary tree node.
2. struct TreeNode {
3. int val;
4. TreeNode *left;
5. TreeNode *right;
6. TreeNode(int x) : val(x), left(NULL), right(NULL) {}
7. };
*/
class Solution { //同王道P119
public:
vector<int> levelOrder(TreeNode* root) {
queue<TreeNode> myQueue;
vector<int> res;
if(root==NULL) return res; //漏了报错,或!root
TreeNode p;
myQueue.push(*root);
while(!myQueue.empty())
{
p = myQueue.front();
myQueue.pop(); //漏了-->超出内存空间/时间限制
res.push_back(p.val);
if(p.left!=NULL)
{
myQueue.push(*p.left);
}
if(p.right!=NULL)
{
myQueue.push(*p.right);
}
}
return res;
}
};
层次遍历Ⅱ
例如:
给定二叉树: [3,9,20,null,null,15,7],
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
分析:
BFS要把每一层放到一起,需要维护一个temp进行保存。并且引入size记录queue长度,即单层节点数目。
DFS使用level记录节点深度再分别插入容器。
BFS:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> myQueue;
vector<vector<int>> myVector;
if(root==NULL) return {};
//也可以 return vector<vector<int>>();
myQueue.push(root);
while(!myQueue.empty()) //或 while(myQueue.size())
{
int size = myQueue.size();
vector<int>tmp;
for(int i=0;i<size;i++)
{
TreeNode* p = myQueue.front();
myQueue.pop();
if(!p) continue;
tmp.push_back(p->val);
if(p->left) myQueue.push(p->left);
if(p->right) myQueue.push(p->right);
}
if(!tmp.empty()) myVector.push_back(tmp);
}
return myVector;
}
};
DFS:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>>res;
dfs(root,res,0);
return res;
}
void dfs(TreeNode* root,vector<vector<int>>&res,int level)
{
if(!root) return;
if(level>=res.size()) res.push_back(vector<int>()); //有它才能进行后续插入
res[level].push_back(root->val);
dfs(root->left,res,level+1);
dfs(root->right,res,level+1);
}
};
层次遍历Ⅲ
例:
给定二叉树: [3,9,20,null,null,15,7]
返回其层次遍历结果:
[
[3],
[20,9],
[15,7]
]
思路:
DFS:使用变量level记录深度,分情况插入。vector容器尾插 push_back();头插 insert(v.begin(),val)。
BDF:size记录queue元素个数,弹出队头:queue.front()
DFS:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>>res;
dfs(root,res,0);
return res;
}
void dfs(TreeNode* root,vector<vector<int>>&res,int level)
{
if(!root) return;
if(level>=res.size()) res.push_back(vector<int>());
if(level%2==0)
{
res[level].push_back(root->val);
}
else
{
res[level].insert(res[level].begin(),root->val);
}
dfs(root->left,res,level+1);
dfs(root->right,res,level+1);
}
};
BFS:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*>myQueue;
vector<vector<int>> res;
if(!root) return res;
int level = 0;
myQueue.push(root);
while(!myQueue.empty())
{
int size = myQueue.size();
vector<int>tmp;
for(int i=0;i<size;i++)
{
TreeNode* p = myQueue.front();
myQueue.pop();
if(level%2==0) tmp.push_back(p->val);
else tmp.insert(tmp.begin(),p->val);
if(p->left) myQueue.push(p->left);
if(p->right) myQueue.push(p->right);
}
level++;
if(!tmp.empty()) res.push_back(tmp);
}
return res;
}
};
排序
排序奇偶数 J21
奇数在前,偶数在后。
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
int left = 0,right = nums.size()-1;
while(left<right)
{
// if(nums[left]%2!=0)
if((nums[left] & 1) != 0) //位运算会比上面的快
{
left++;
continue;
}
// if(nums[right]%2==0)
if((nums[right] & 1) == 0)
{
right--;
continue;
}
// swap(nums[left],nums[right]);
swap(nums[left++],nums[right--]);
}
return nums;
}
};
编译时候的问题
warning: control reaches end of non-void function
控制到达非void函数的结尾。就是说你的一些本应带有返回值的函数到达结尾后可能并没有返回任何值。这时候,最好检查一下是否每个控制流都会有返回值。