LeetCode -- 力扣算法题解题心得 -- (个人笔记记录)持续更新~~

一、前言

正式开启数据结构+算法研究的历程,准备好一年后的面试。下面的解法不一定是最优解,只求能力提升,会定期更新~~

二、目录

1219202124
354283869496
100101102103104108
110111112
122136141142144155
160
191202203206226231
235237257268371389
404559563876
9385673

三、题解部分

1.两数之和

(暴力算法)
此题目类似冒泡排序法,使用暴力算法解题,双重循环遍历求得目标值,每次循环判断是否满足条件,如果满足,则记录下标。(返回结果的时候注意返回returnSize)。
在这里插入图片描述

/**
 1. Note: The returned array must be malloced, assume caller calls free().
 */
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
    int *a = (int *)malloc(sizeof(int)*2);
    for (int i = 0; i < numsSize-1; i++) {
        for (int j = i + 1; j < numsSize; j++) {
            if (nums[j] + nums[i] == target) {
                a[0] = i;
                a[1] = j;
                *returnSize = 2;
                return a;
            }
        }
    }
    *returnSize = 0;
    return 0;
}

2.两数相加

代码可能写的比较啰嗦,但是逻辑是没问题的,每位相加记录进位数,直到两个都结束,结束后检查进位填补结点。
在这里插入图片描述

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* res = new ListNode();
        ListNode rest;
        rest.next = res;
        int memo = 0;
        while(l1 != NULL && l2 != NULL) {
            int temp = memo + l1->val + l2->val;
            if(temp > 9){
                memo = 1;
            }else {
                memo = 0;
            }
            ListNode* kk = new ListNode();
            kk->val = (temp) % 10;
            res->next = kk;
            res = res->next;
            l1 = l1->next;
            l2 = l2->next;
        }

        while(l1 != NULL){
            int temp = memo + l1->val;
            if(temp > 9){
                memo = 1;
            }else {
                memo = 0;
            }
            ListNode* kk = new ListNode();
            kk->val = (temp) % 10;
            res->next = kk;
            res = res->next;
            l1 = l1->next;
        }
        while(l2 != NULL){
            int temp = memo + l2->val;
            if(temp > 9){
                memo = 1;
            }else {
                memo = 0;
            }
            ListNode* kk = new ListNode();
            kk->val = (temp) % 10;
            res->next = kk;
            res = res->next;
            l2 = l2->next;
        }
        if(memo == 1){
            ListNode* kk = new ListNode();
            kk->val = 1;
            kk->next = NULL;
            res->next = kk;
        }
        return rest.next->next;
    }
};

19.删除链表的倒数第N个结点

这道题用暴力的方法,注意要添加一个虚拟头结点来处理链表为空或者为一个结点的情况。
这里也可以用双指针的方法一遍过,让两个间距为n的指针平行移动找到要删除的即可,这里就不实现了。睡觉了,晚安~
在这里插入图片描述

struct ListNode* removeNthFromEnd(struct ListNode* head, int n){  

    struct ListNode* test = (struct ListNode*)malloc(sizeof(struct ListNode));
    test->next = head;

    struct ListNode p;          // 用一个结点p来记录链表
    p.next = test; 
    int num = 0;
    
    while(head!=NULL){
        num++;
        head = head->next;
    }
    
    for(int i=0;i<num-n;i++){
        test = test->next;
    }

    if(test->next!=NULL){
        test->next = test->next->next;
    }
    return p.next->next;
}

20.有效的括号

这里用了线性栈–一个数组来表示栈,将左括号放入栈中,计数器加一,当遇到右括号的时候进行匹配,并且使计数器的值减一,最终判断计数器的值是否被清空。
在这里插入图片描述

bool isValid(char * s){
    int n = strlen(s);
    char *arr = (char *)malloc(sizeof(char)*n+1);
    memset(arr,0,n);
    int j = 1;
    for(int i = 0;i<n;i++){
        if(s[i] == '{' || s[i] == '(' || s[i] == '['){
            arr[j] = s[i];
            j++;
        }else if((s[i] == '}' && arr[j-1] == '{') || (s[i] == ')' && arr[j-1] == '(') || (s[i] == ']' && arr[j-1] == '[')){
                j--;
        }else{
            return false;
        }
    }

    if(j == 1){
        return true;
    }

    return false;
}

21.合并两个有序链表

1.自用的方法是暴力算法,性能很低下。。。最古老的办法进行链表的逐个排序放入,注意操作链表的写法。
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
    struct ListNode *temp = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *Head = (struct ListNode *)malloc(sizeof(struct ListNode));
    temp->next = NULL; 

    if(l1->val < l2->val){
        struct ListNode *swap = (struct ListNode *)malloc(sizeof(struct ListNode));
        swap->val = l1->val;
        temp->next = swap;
        temp = temp->next;
    }else if(l1->val >= l2->val){
        struct ListNode *swap = (struct ListNode *)malloc(sizeof(struct ListNode));
        swap->val = l2->val;
        temp->next = swap;
        temp = temp->next;
    }

    return Head;
}

24.两两交换链表中的结点

题目不是很难,但是反映的问题比较多,首先要记住虚拟头结点的重要性,然后链表换顺序需要一个虚拟头结点记住初始位置。
在这里插入图片描述

struct ListNode* swapPairs(struct ListNode* head){
    struct ListNode p;
	p.next = head;
	struct ListNode* temp = &p;
    while((temp->next!=NULL)&&(temp->next->next!=NULL)){
    	struct ListNode* s1 = temp->next;
    	struct ListNode* s2 = temp->next->next;
    	temp->next = s2;
    	s1->next = s2->next;
    	s2->next = s1;
		temp = temp->next->next;
	} 
	return p.next;
}

35.搜索插入位置

一道简单题,用普通的搜索方法就可以做出;
可用来复习二分法查找。
在这里插入图片描述

int searchInsert(int* nums, int numsSize, int target){
    int i;
    for(i = 0;i<numsSize;i++){
        if(nums[i] == target){
            return i;
        }
        if(nums[i] > target){
            return i;
        }
    }
    
    return i;
}

42.接雨水

一道困难的题目,暴力的办法很奏效,效率比较低,每个X扫描一次左右边界。
在这里插入图片描述

class Solution {
public:
    int trap(vector<int>& height) {
        int left,right;
        int res = 0;

        for(int i=0;i<height.size();i++){
            left = 0,right = 0;
            // 每次进行一次循环,选出左右边界值
            for(int j=0;j<height.size();j++){
                int val = height[j];
                if(j<i && val>left){
                    left = val;
                }
                if(j>i && val>right){
                    right = val;
                }
            }
            int num = min(left,right);
            if(num > height[i]){
                res += num-height[i];
            }
        }
        return res;
    }
};

83.删除排序链表中的重复元素

遍历每个结点,判断是否重复。
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

struct ListNode* deleteDuplicates(struct ListNode* head){
    struct ListNode* cur = head;
    while(cur != NULL){
        struct ListNode* rear = cur->next;
        if(rear == NULL){
            return head;
        }
        if(cur->val == rear->val){
            cur->next = rear->next;
        }
        else{
            cur = cur->next;
        }
    }
    return head;
}

86.分隔链表

这题实现起来比较麻烦,需要不停地维护两个结点,并且需要一个标志来记录是否出现大于等于特殊值的结点。代码最终实现效果很好,几乎都是超过所有人。
在这里插入图片描述

struct ListNode* partition(struct ListNode* head, int x){
    struct ListNode* temp = (struct ListNode*)malloc(sizeof(struct ListNode));
    temp->next = head;
    struct ListNode p;
    p.next = temp;
    
    bool flag = false;		// 用来标志是否出现大于等于特殊值的结点,若没出现,前面不用动
    struct ListNode* ram;	// 用来标志前面最新的小节点,即下一次前移要插进该结点的后面
    struct ListNode* rbm;	// 用来标志第一次出现的大于等于特殊值的结点
    ram = temp;				// 排除链表结点个数少的情况
    
    while(temp->next != NULL){
        if((temp->next->val<x)&&!flag){		
        	// 未出现大于等于特殊值的结点,一直往后推进,同时不断更新ram
            ram = temp->next;
            temp = temp->next;
        }else if(temp->next->val>=x){
        	// 出现了特殊结点,修改标志,记录rbm
			// 后续再出现,不做任何改动
            if(!flag){
                flag = true;
                rbm = temp->next;
            }
            temp = temp->next;
        }else if((temp->next->val<x)&&flag){
        	// 已出现特殊结点且当前结点小于特殊值,做顺序修改,具体过程建议画图
            ram->next = temp->next;
            temp->next = temp->next->next;
            ram->next->next = rbm;
            ram = ram->next;
        }
    }
    return p.next->next;
}

94.二叉树的中序遍历

本题有两种做法,一种是中规中矩的递归,另一种是看官方题解给的方法,叫做Mirrors方法,代码附在下面,目前还没有办法能写出这个算法。。。。加油。
在这里插入图片描述

void inorder(struct TreeNode* root,int *arr,int *num){
    if(root == NULL){
        return;
    }
    inorder(root->left,arr,num);
    arr[(*num)++] = root->val;
    inorder(root->right,arr,num);
}

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* inorderTraversal(struct TreeNode* root, int* returnSize){
    int *arr = malloc(sizeof(int)*500);
    *returnSize = 0;
    inorder(root,arr,returnSize);    
    return arr;
}
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    int* res = malloc(sizeof(int) * 501);
    *returnSize = 0;
    struct TreeNode* predecessor = NULL;

    while (root != NULL) {
        if (root->left != NULL) {
            // predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止
            predecessor = root->left;
            while (predecessor->right != NULL && predecessor->right != root) {
                predecessor = predecessor->right;
            }

            // 让 predecessor 的右指针指向 root,继续遍历左子树
            if (predecessor->right == NULL) {
                predecessor->right = root;
                root = root->left;
            }
            // 说明左子树已经访问完了,我们需要断开链接
            else {
                res[(*returnSize)++] = root->val;
                predecessor->right = NULL;
                root = root->right;
            }
        }
        // 如果没有左孩子,则直接访问右孩子
        else {
            res[(*returnSize)++] = root->val;
            root = root->right;
        }
    }
    return res;
}

96.不同的二叉搜索树

这道题乍一看像是一个递归求解的问题,但实际上是一道数学题,考虑到动态规划,详细解法列式推导。
在这里插入图片描述

class Solution {
public:
    int numTrees(int n) {
            vector<int> G(n+1,0);
            G[0] = 1;
            G[1] = 1;

            for(int i = 2;i <= n;i++) {
                for(int j=0;j<i;j++) {
                    G[i] += G[j]*G[i-1-j];
                }
            }
            return G[n];
        }
};

100.相同的树

(递归算法)
1.先判断节点是否拥有左右孩子,同时为空则返回true,只有一个则返回false
2.如果两个节点的值不相等,则返回false
3.如果没有return,函数最终利用递归法,将对应的左孩子和右孩子进行递归返回值。
在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    if(p == NULL && q == NULL){
        return true;
    }
    
    if(p == NULL || q == NULL){
        return false;
    }
    
    if(p->val != q->val){
        return false;
    }
    return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}

101.对称二叉树

(递归算法)
此题应该特别写一个函数,函数含有两个参数,左子树和右子树,函数内部使用递归。
1.先判断传入的两个节点是否同时为空,或者其中一个为空,前者return true,后者return false
2.如果上述过程没有return,则判断左节点的右孩子和右节点的左孩子&&右节点的右孩子和左节点的左孩
最重要的是找到递归点。
在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

bool checkSymmetric(struct TreeNode* lroot,struct TreeNode* rroot){
    if(lroot == NULL && rroot == NULL){
        return true;
    }

    if(lroot == NULL || rroot == NULL){
        return false;
    }

    if(lroot->val == rroot->val){
        return checkSymmetric(lroot->left,rroot->right)&&checkSymmetric(lroot->right,rroot->left);
    }

    return false;
}

bool isSymmetric(struct TreeNode* root){
    return checkSymmetric(root,root);
}

102.二叉树的层序遍历①

肝了两天,解决了这个层序遍历的个人解法!
我的思路是递归遍历层数的时候带上一个层数记录器,利用这个记录器将结点信息保存在二维容器对应层。先是用C语言做,很不理想,因为C没有动态容器这种东西,后来使用了C++的vector,很轻松的解决了,算法速度很快,比队列快了一丢丢,优点在于,思路极其简单!
在这里插入图片描述

// 个人解法:
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
		vector<vector<int>> res;
		int pos = 0;
		Tranverse(root,pos,res);
		return res;
    }

	void Tranverse(TreeNode* root,int pos,vector<vector<int>> &arr){
		if(root == NULL){
			return;
		}
		if(pos>=arr.size()){
			arr.push_back(vector<int> ());
		}
		arr[pos].push_back(root->val);
		Tranverse(root->left,pos+1,arr);
		Tranverse(root->right,pos+1,arr);
	}
};

103.二叉树的锯齿形层序遍历

和上面几个层序遍历类似,这题的思路是增加一个层数指示器,按照正常的顺序存进队列,然后按层存入容器,偶数层逆序存入容器。
在这里插入图片描述

class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> res;
        queue<TreeNode*> q;
        int level = 0;

        if(root == NULL){
            return res;
        }

        q.push(root);
        while(!q.empty()){
            vector<int> newbar;
            int size = q.size();
            for(int i=0;i<size;i++){
                TreeNode* cur = q.front();
                q.pop();
                newbar.push_back(cur->val);
                if(cur->left != NULL){
                    q.push(cur->left);
                }
                if(cur->right != NULL){
                    q.push(cur->right);
                }
            }
            if(level++ % 2 == 1){
                reverse(newbar.begin(),newbar.end());
            }
            res.push_back(newbar);
        }
        return res;
    }
};

104.二叉树的最大深度

定义一个max函数,递归遍历每一个叶子节点,经过一个就加一,return最大的数。
在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

int max(int a,int b){
    if(a>b){
        return a;
    }else{
        return b;
    }
}

int maxDepth(struct TreeNode* root){
    if(root == NULL){
        return 0;
    }

    return 1 + max(maxDepth(root->left),maxDepth(root->right));
}

107.二叉树的层序遍历②

本题类似102题,102题我用的是自己的方法,那么这题用的就是基础的队列方法,没啥好说的看代码。
在这里插入图片描述

class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        queue<TreeNode*> q;
        q.push(root);
        vector<vector<int>> smallv;
        
        if(root == NULL){
            return smallv;
        }

        while(!q.empty()){
            int size = q.size();
            vector<int> newv;
            for(int i=0;i<size;i++){
                struct TreeNode* cur = q.front();
                q.pop();
                newv.push_back(cur->val);
                if(cur->left != NULL){
                    q.push(cur->left);
                }
                if(cur->right != NULL){
                    q.push(cur->right);
                }
            }
            smallv.push_back(newv);
        }
        reverse(smallv.begin(),smallv.end());
        return smallv;
    }
};

108.将有序数组转换为二叉搜索树

先看题目,这道题给出的是一个有序数组,所以我们只要每次选择最中间的元素视为根结点返回即可,过程中需要控制边界。
在这里插入图片描述

struct TreeNode* constructor(int* nums,int left,int right){
    if(left > right){
        return NULL;
    }

    struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    int mid = (left + right + 1)/2;

    root->val = nums[mid];
    root->left = constructor(nums,left,mid-1);
    root->right = constructor(nums,mid+1,right);

    return root;
}


struct TreeNode* sortedArrayToBST(int* nums, int numsSize){
    return constructor(nums,0,numsSize-1);
}

110.平衡二叉树

思路比较简单,平衡二叉树的每一棵子树都是平衡二叉树。使用双重递归的方法,目前比较复杂,等待以后优化。
在这里插入图片描述

int height(struct TreeNode* root){
    if(root == NULL){
        return 0;
    }
    return fmax(height(root->left),height(root->right))+1;
}

bool isBalanced(struct TreeNode* root){
    if(root == NULL){
        return true;
    }
    return (fabs(height(root->left)-height(root->right)) <= 1)&&(isBalanced(root->left))&&(isBalanced(root->right)); 
}

111.二叉树的最小深度

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */


int minDepth(struct TreeNode* root){
    if(root == NULL){
        return 0;
    }

    if(root->left == NULL && root->right == NULL){
        return 1;
    }

    if(root->left != NULL && root->right == NULL){
        return 1+minDepth(root->left);
    }

    if(root->left == NULL && root->right != NULL){
        return 1+minDepth(root->right);
    }

    return 1+(minDepth(root->left)<minDepth(root->right)?minDepth(root->left):minDepth(root->right));
}

112.路径总和

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

bool hasPathSum(struct TreeNode* root, int sum){
    if(root == NULL){
        return false;
    }
    
    if(root->left == NULL && root->right == NULL){
        return root->val == sum;
    }

    return hasPathSum(root->left,sum-root->val) || hasPathSum(root->right,sum-root->val);
}

122.买卖股票的最佳时机

使用贪心算法,sum每符合条件的两天之间的差值。
在这里插入图片描述

int maxProfit(int* prices, int pricesSize){
    if(pricesSize == 0){
        return 0;
    }
    int sum = 0;
    for(int i = 0;i < pricesSize - 1;i++){
        if(prices[i]<prices[i+1]){
            sum += prices[i+1] - prices[i];
        }
    }
    return sum;
}

136.只出现一次的数字

第一次使用位运算,还用到了异或^,太骚了,这三行代码解决。
任何数与0异或结果为本身,
任何数与自身异或结果为0;
在这里插入图片描述

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res = 0;
        for(auto i:nums) res ^= i;
        return res;
    }
};

141.环形链表

本题适合使用快慢指针来求解,如果存在环,快指针总会撞到慢指针。
在这里插入图片描述

bool hasCycle(struct ListNode *head) {
    if(head == NULL || head->next == NULL){
        return false;
    }
    struct ListNode *slow = head;
    struct ListNode *fast = head->next;
    while((fast != NULL) && (fast->next != NULL)){
        slow = slow->next;
        fast = fast->next->next;
        if(fast==slow){
            return true;
        }
    }
    return false;
}

142.环形链表②

本题较此题的母题加大了难度,母题判断是否有环,本题则返回环的入口结点。
同样是使用快慢指针的思路,快指针一次走两步,慢指针一次走一步,在环内有一个相遇点。
先进行数学归纳推导:
如图所示,A为环前表长,B为相遇点前的长度,C为相遇点到入口点的长度。
假设n为在圈内循环的次数,则:
先分析快指针步数:
Step1 = A + n × ( B + C ) + B = A + ( n + 1 ) × B + n × C
再分析慢指针步数:
Step2 = A + B
两者数量关系为2倍: Step2 × 2 = Step1
则 2 ×( A + B ) = A + ( n + 1 ) × B + n × C
得到
A =( n - 1 )× ( B + C )+ C
即入口前长度A等于从n圈后加C,初始位置为相遇点,则n圈后加C的位置为入环结点。
于是我们让一个新的结点从head开始和slow一起跑,最终相遇在入环结点。
在这里插入图片描述

在这里插入图片描述

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *slow = head;
    struct ListNode *fast = head;
    while(fast!=NULL){
        if(fast->next == NULL) return NULL;
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast){   
            struct ListNode *extra = head;
            while(extra!=slow){
                slow = slow->next;
                extra = extra->next;
            }
            return extra;
        }
    }
    return NULL;
}

144.二叉树的前序遍历

nnd,好不容易会了递归,还有迭代。。。好不容易会了迭代,还有Morris。。。。。
在这里插入图片描述

class Solution {
public:
    void Tranverse(TreeNode* root,vector<int> &res) {
        if(root == nullptr) {
            return;
        }
        res.push_back(root->val);    
        Tranverse(root->left,res);
        Tranverse(root->right,res);
    }

    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        Tranverse(root,res);
        return res;
    }   
};

155.最小栈

在栈中使用到了辅助栈,负责记录主栈的最小值,起到用空间换取时间的效果。如果不用辅助栈,算法性能总体都很差。
在这里插入图片描述

typedef struct {
    int arr[7700];
    int top;
    struct numstack *stack;
}MinStack;

typedef struct numstack{
    int arr[3000];
    int top;
}numstack;

/** initialize your data structure here. */

MinStack* minStackCreate() {
    MinStack *st = (MinStack *)malloc(sizeof(MinStack));
    st->stack = (numstack *)malloc(sizeof(numstack));
    st->top = -1;
    st->stack->top = -1;
    return st;
}

void minStackPush(MinStack* obj, int x) {
    obj->top ++;
    obj->arr[obj->top] = x;
    if(obj->top == 0){
        obj->stack->top++;
        obj->stack->arr[obj->stack->top] = x;
    }else{
        if(x<=obj->stack->arr[obj->stack->top]){
            obj->stack->top++;
            obj->stack->arr[obj->stack->top] = x;
        }
    }
}

void minStackPop(MinStack* obj) {
    if(obj->arr[obj->top] == obj->stack->arr[obj->stack->top]){
        obj->stack->top--;
    }
    obj->top--;
}

int minStackTop(MinStack* obj) {
    int i = obj->top;
    return obj->arr[obj->top];
}

int minStackGetMin(MinStack* obj) {
    if(obj->stack->top == -1){
        return NULL;
    }
    return obj->stack->arr[obj->stack->top];
}

void minStackFree(MinStack* obj) {
    free(obj);
}

/**
 * Your MinStack struct will be instantiated and called as such:
 * MinStack* obj = minStackCreate();
 * minStackPush(obj, x);
 
 * minStackPop(obj);
 
 * int param_3 = minStackTop(obj);
 
 * int param_4 = minStackGetMin(obj);
 
 * minStackFree(obj);
*/

160.相交链表

本题比较浪漫的解法,类似快慢指针寻找环,本题是两个指针错位移动寻找共同节点。
正所谓:错的人终将走散,对的人终将重逢。
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode *work1 = headA;
    struct ListNode *work2 = headB;
    while(work1!=work2)
    {
        if(work1)
            work1 = work1->next;
        else
            work1 = headB;
        if(work2)
            work2 = work2->next;
        else
            work2 = headA;
    }
    return work2;
}

190.颠倒二进制位

本题使用位运算,从右往左将每个位赋值给新的变量。
在这里插入图片描述

class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        uint32_t res = 0;
        int num = 31;
        while(n!= 0){
            res += (n & 1) << num--;
            n = n >> 1;
        }
        return res;
    }
};

191.位1的个数

趁热打铁,学了使用位运算,真的快活!下面给出两种方法,第一种是自己想出来的,第二种是官网优化算法。只能说,太巧妙了,将n与n-1进行或运算总会将最小的1位变为0。
在这里插入图片描述

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int num = 0;
        while(n != 0){
            if(n & 1 == 1){
                num++;
            } 
            n = n >> 1;
        }
        return num;
    }
};
class Solution {
public:
    int hammingWeight(uint32_t n) {
        int num = 0;
        while(n != 0){
            num++;
            n &= (n - 1);
        }
        return num;
    }
};

202.快乐数

有三种做法:1.自己的解法,贪心算法;2.哈希表解法;3.快慢指针的解法。
我的解法有两个判断条件:根据观察,递归时:1.若某次递归初始数为1,则返回 true ;2.若某次递归时初始数为4,则陷入死循环,不快乐了,返回 false 。
其中我的解法要注意的知识点是:未知位数的 int 型变量取出每位的数,用%从小往大取比较方便。
在这里插入图片描述

bool isHappy(int n){
    if(n==1){
        return true;
    }

	if(n==4){
		return false;
	}

    int j=0;
    int temp = n;
    while(temp>0){
        temp = temp/10;
        j++;
    }
	
    int k=0;
	for(int i=0;i<j;i++){
		k = k + (n%10) * (n%10);
		n = n / 10; 
	}

    return isHappy(k);
}

203.移除链表元素

在这里插入图片描述

struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode p;
	p.next = head;	

    struct ListNode* cur = &p;
    while (cur->next != NULL) {
        if (cur->next->val == val) {
            cur->next = cur->next->next;
        } else {
            cur = cur->next;
        }
    }
    return p.next;
}

206.翻转链表链表

让链表中每个结点回指向前一个结点。
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* pre = NULL;
    struct ListNode* current = head;
    struct ListNode* temp;
    while(current != NULL){
        temp = current->next;
        current->next = pre;
        pre = current;
        current = temp;
    }
    return pre;
}

226. 翻转二叉树

利用递归思想很简单就可以解决。
在这里插入图片描述

/**
 1. Definition for a binary tree node.
 2. struct TreeNode {
 3.     int val;
 4.     struct TreeNode *left;
 5.     struct TreeNode *right;
 6. };
 */
 
struct TreeNode* invertTree(struct TreeNode* root){
    if(root == NULL){   
        return NULL;
    }

    struct TreeNode* temp = root->left;
    root->left = root->right;
    root->right = temp;
    invertTree(root->left);
    invertTree(root->right);
    return root;
}

231.2的幂

这题给的测试数据太坑了,题目也没有说清楚,需要注意数据类型和长度,然后要学会使用n&n-1。
在这里插入图片描述

class Solution {
public:
    bool isPowerOfTwo(int n) {
        if(n == 0){
            return false;
        }
        long x = n;
        return ((x-1) & x) == 0;
    }
};

235.二叉搜索树的最近公共祖先

此题有两种解法,一种是两遍遍历得到两个结点的访问路径,然后逐个比较筛选;
另一种解法是:
有三种情况:

  1. 当前结点的值大于两个结点的值,则访问左子树;
  2. 当前结点的值小于两个结点的值,则访问右子树;
  3. 当前结点的值在两个结点值中间,则为分歧点,直接跳出循环返回。
    在这里插入图片描述
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
    while(true){
        if(root->val>p->val&&root->val>q->val){
            root = root->left;
        }else if(root->val<p->val&&root->val<q->val){
            root = root->right;
        }else{
            break;
        }
    }
    return root;
}

237.删除链表中的节点

此题较为简单,直接把节点拉出来斩了就行。
在这里插入图片描述

void deleteNode(struct ListNode* node) {
    node->val = node->next->val;
    node->next = node->next->next;
}

257.二叉树的所有路径

采用深度遍历算法,用一个数组记录非叶结点,当访问到叶结点时将相应的序列放进数组。
在这里插入图片描述

void Tranverse(struct TreeNode* root,int num,int *arr,int *returnSize,char** res){
    if(root == NULL){
        return ;
    }
    if(root->left == NULL && root->right == NULL){
        char* temp = (char*)malloc(sizeof(char)*1000);
        int len = 0;
        for(int i=0;i<num;i++){
            len += sprintf(temp+len,"%d->",arr[i]);
        }
        sprintf(temp+len,"%d",root->val);
        res[(*returnSize)++] = temp;
    }else{
        arr[num++] = root->val;
        Tranverse(root->left,num,arr,returnSize,res);
        Tranverse(root->right,num,arr,returnSize,res);
    }
}

char ** binaryTreePaths(struct TreeNode* root, int* returnSize){
    char** res = (char**)malloc(sizeof(char*)*1000);
    int* arr = (int*)malloc(sizeof(int)*1000);
    *returnSize = 0;
    Tranverse(root,0,arr,returnSize,res);
    return res;
}

268.缺失数字

一种做法是付出额外的空间,申请一个额外的数组来存放数字,然后改变值,通过值来判断是否缺失。
在这里插入图片描述

int missingNumber(int* nums, int numsSize){
    int *arr = (int*)malloc(sizeof(int)*(numsSize+1));
    int ans;
    for(int i=0;i<numsSize+1;i++){
        arr[i] = -1;
    }
    for(int i=0;i<numsSize;i++){
        arr[nums[i]] += 1;
    }
    for(int j=0;j<numsSize+1;j++){
        if(arr[j] != 0){
            ans = j;
            break;
        }
    }
    return ans;
}

342.4的幂

该题目是2的幂的变题,只需要在辨别2的幂的基础上区分2和4,两种思路,一是用该数异运算0xaaaaaaaa,得到一的为4的幂,可以发散至其他的题目。但是也可用%3 == 1 的方法。
在这里插入图片描述

class Solution {
public:
    bool isPowerOfFour(int n) {
        long x = n;
        if(n <= 0) return false;
        return ((n-1 & n) == 0) && (n%3==1);
    }
};

371.两整数之和

利用加法器的原理,先用异或算加过的位数,再用或算出进位,循环,直到为零。
在这里插入图片描述

class Solution {
public:
    int getSum(int a, int b) {
        unsigned long x = a,y = b;
        while(x != 0){
            long temp = x^y;
            x = (x&y) << 1;
            y = temp;
        }
        return y;
    }
};

389.找不同

本题思路同样是位运算,异或较为简单。
在这里插入图片描述

class Solution {
public:
    char findTheDifference(string s, string t) {
        int res = 0;
        for(char ch: s){
            res ^= ch;
        }
        for(char kh: t){
            res ^= kh;
        }
        return (char)res;
    }
};

404.左叶子之和

本题官方解法为BFS和DFS,本人没有使用这两种厚重的方法,而是使用了一个判断左右的标志flag,如果结点不为叶子结点,则他的左节点标记为1,右节点标记为0,当结点为叶子结点且标记为1的时候,才符合题意,进行累加。
在这里插入图片描述

class Solution {
public:
    void getsum(TreeNode* root,int status,int &num) {
        if(root == NULL) {
            return;
        }

        if(root->left == NULL && root->right == NULL && status == 1) {
            num += root->val;
        }

        getsum(root->left,1,num);
        getsum(root->right,0,num);
    }

    int sumOfLeftLeaves(TreeNode* root) {
        int num = 0;
        getsum(root,0,num);
        return num;
    }
};

559.N叉树的最大深度

这道题类似于二叉树的最大深度,需要遍历孩子递归,采用深度优先遍历,主要是处理最大数要思考一下。掌握了该题的方法就掌握了一类题目。
在这里插入图片描述

class Solution {
public:
    int gostep(Node* root) {
        if(root == NULL) {
            return 0;
        }

        if(root->children.size() == 0) {
            return 1;
        }

        int max = 0;
        for(Node *temp : root->children) {
            int k = maxDepth(temp);
            if(max < k) {
                max = k;
            }
        }
        return max + 1;
    }

    int maxDepth(Node* root) {
        int res = gostep(root);
        return res;
    }
};

563.二叉树的坡度

本题使用递归,从下往上逐层扫描,注意最后面return的数为左右结点的和,而每次记录的是左右结点差的绝对值。
在这里插入图片描述

class Solution {
public:
    int res = 0;
    int recursion(TreeNode *root) {
        if(root == NULL) {
            return 0;
        }

        int left = recursion(root->left);
        int right = recursion(root->right);
        res += abs(left-right);
        return left+right+root->val;
    }

    int findTilt(TreeNode* root) {
        recursion(root);
        return res;
    }
};

876.链表的中间结点

本题两种思路,一种是暴力求解,先得到链表长度,再返回一半的链表;
另一种是用快慢指针,慢指针走到一半快指针走到头。下面给出了两种方法的代码。原则上两种方法的性能是一样的,但第二种方法更加巧妙,且快慢指针可以处理其他很多问题。
在这里插入图片描述

// 暴力求解
struct ListNode* middleNode(struct ListNode* head){
    int num = 0;
    struct ListNode* test = head;
    while(test!=NULL){
        num++;
        test = test->next;
    }
    for(int i=0;i<num/2;i++){
        head = head->next;
    }
    return head;
}

// 快慢指针
struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while((fast != NULL)&&(fast->next != NULL)){
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

938.二叉搜索树的范围和

第一次没好好读题,做成了普通二叉树的解法,给出代码,蛮简单的,然后又改成二叉搜索树的代码。结果显示,性能还不如暴力解法。
在这里插入图片描述


int Tranverse(struct TreeNode* root,int low,int high){
    if(root == NULL){
        return 0;
    }

    int k = 0;
    if(root->val >= low && root->val <= high){
       k = root->val;
    }

    return k + Tranverse(root->left,low,high) + Tranverse(root->right,low,high);
}

int rangeSumBST(struct TreeNode* root, int low, int high){
    int k = Tranverse(root,low,high);
    return k;
}

int Tranverse(struct TreeNode* root,int low,int high){
    if(root == NULL){
        return 0;
    }

    // 进行三重选择判断
    if(root->val < low){
        return 0 + Tranverse(root->right,low,high);
    }else if(root->val > high){
        return 0 + Tranverse(root->left,low,high);
    }
    
    return root->val + Tranverse(root->left,low,high) + Tranverse(root->right,low,high);

}

int rangeSumBST(struct TreeNode* root,int low,int high){
    int k = Tranverse(root,low,high);
    return k;
}

5672.检查数组是否经排序和轮换得到

这题思路就是将数组进行n次轮转,每次从后往前挪动一个元素,然后检查是否升序排列。
在这里插入图片描述

bool check(int* nums, int numsSize){
    int i;
    for(int j=0;j<numsSize;j++){
    	int num =0;
        int k = nums[numsSize-1];
        for(int m = numsSize-2;m>=0;m--){
            nums[m+1] = nums[m];
        }
        nums[0] = k;
		for(i=0;i<numsSize-1;i++){
            if(nums[i]<=nums[i+1]){
                num++;
            }
        }
        if(num == numsSize-1){
            return true;
        }
    }
    return false;
}

5673.移除石子的最大得分

第一次参加力扣周赛,肝了两题还都是暴力,害,事后来诸葛亮了。
在这里插入图片描述

int maximumScore(int a, int b, int c){
        if(a+b<=c)
            return a+b;
        else if(a+c<=b)
            return a+c;
        else if(b+c<=a)
            return b+c;
        return (a+b+c)/2;
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor时,有时候会出现乱码的问。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有时候,乱码问可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问。 4. 查找特殊字符:如果乱码问只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张甜不拉几

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值