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

一、前言

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

二、目录

由于题量过大,这里给出已做目录:

1 19 20 21 24 35
83 86 94 100 101 104
110 111 112 122 141 142
155 160 202 203 206 226
237 268 876

三、题解部分

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;
}

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;
}

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;
}

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);
}

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));
}

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;
}

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;
}

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;
}

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. 翻转二叉树

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

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
 
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;
}

237.删除链表中的节点

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

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

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;
}

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;
}

猜你喜欢

转载自blog.csdn.net/qq_34533266/article/details/107893449