• leetcode 题解


    92. 反转链表 II

    方法一:

    提交代码:

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     struct ListNode *next;
     * };
     */
    // 由于leetcode提交默认的是没有头结点的,故需要内部创建一个头结点方便使用 struct ListNode* reverseBetween(struct ListNode* head, int m, int n) { if(n < m) return; if(n == m) return head; struct ListNode h = {0, head}; //设置一个头节点【普通结构体变量】,处理m=1的情况 struct ListNode* pre = &h; //指针变量赋值 struct ListNode *p = pre ->next,*q,*t; int i = 1; while(i < m) { pre = p; p = p->next; ++i; } t = p; //t 记录翻转部分的起点 if(m < n) { p = p->next; ++i; } while(i <= n) { q = p; p = p->next; ++i; q->next = pre->next; pre->next = q; // 每次将第一步找到的结点指向反转后的头结点 } t->next = p; //将反转的起点next指向反转后面的结点 return h.next; }

    codeblocks中运行代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct Node
    {
        int data;
        struct Node* next;
    } Node;
    void create(Node *head)
    {
        //创建链表 【尾插法】
        Node *temp,*p;
        int x;
        printf("请输入链表元素:
    ");
        scanf("%d",&x);
        p = head;   //p为尾结点
        while(x != 9999)
        {
            temp = (Node *)malloc(sizeof(Node));
            temp ->data = x;
            p ->next = temp;
            p = p ->next;
            scanf("%d",&x);
        }
        temp ->next = NULL;
    }
    void input(Node *head)
    {
        Node *p = head ->next;  //p为工作指针
        while(p != NULL)
        {
            printf("%d	",p ->data);
            p = p ->next;
        }
    }
    int reverseBetween(Node *head,int m,int n)
    {
        if(n < m)
            return 0;
        if(n == m)
            return head;
        Node *pre,*p,*q,*t;
        int i = 1;
        p = head->next;
        pre = head;
        while(i < m)
        {
            pre = p;
            p = p->next;
            ++i;
        }
        t = p;  //t 记录翻转部分的起点
        if(m < n)
        {
             p = p->next;
             ++i;
        }
        while(i <= n)
        {
            q = p;
            p = p->next;
            ++i;
            q->next = pre->next;
            pre->next = q;   // 每次将第一步找到的结点指向反转后的头结点
        }
        t->next = p;  //将反转的起点next指向反转后面的结点
        return head;
    }
    int main()
    {
        Node * head;
        head = (Node *)malloc(sizeof(Node));  //head为头结点
        head ->data = NULL;
        head ->next = NULL;
        create(head);
        input(head);
        printf("
    逆置后:
    ");
        head = reverseBetween(head,2,5);
        input(head);
    }
    

    方法二:

    提交代码:

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     struct ListNode *next;
     * };
     */
    struct ListNode* reverseBetween(struct ListNode* head, int m, int n) {
        if(m == n)
            return head; // 不用管的情况
        struct ListNode h = {0, head}; //设置一个头节点,处理m=1的情况
        struct ListNode* p = &h;  //指针变量赋值
        struct ListNode* tail;
        for(int i = 1; i <= n; i++)
            if(i < m) // p指向第m-1个节点位置
                p = p->next;
            else if(i == m) // tail指向第第m个节点,这个节点反转后处在反转部分的最后一个
                tail = p->next;
            else { //每次将tail后面一个节点拿出来,放在tail后面
                struct ListNode* item = tail->next;
                tail->next = tail->next->next;
                item->next = p->next;
                p->next = item;
            }
        return h.next;
    }
    

    codeblocks中运行代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct Node
    {
        int data;
        struct Node* next;
    } Node;
    void create(Node *head)
    {
        //创建链表 【尾插法】
        Node *temp,*p;
        int x;
        printf("请输入链表元素:
    ");
        scanf("%d",&x);
        p = head;   //p为尾结点
        while(x != 9999)
        {
            temp = (Node *)malloc(sizeof(Node));
            temp ->data = x;
            p ->next = temp;
            p = p ->next;
            scanf("%d",&x);
        }
        temp ->next = NULL;
    }
    void input(Node *head)
    {
        Node *p = head ->next;  //p为工作指针
        while(p != NULL)
        {
            printf("%d	",p ->data);
            p = p ->next;
        }
    }
    int reverseBetween(Node *head,int m,int n)
    {
        if(n < m)
            return 0;
        if(m == n)
            return head; // 不用管的情况
        struct Node* p = head,* tail;
        int i ;
        for(i = 1; i <= n; i++)
            if(i < m) // p指向第m-1个节点位置
                p = p->next;
            else if(i == m) // tail指向第第m个节点,这个节点反转后处在反转部分的最后一个
                tail = p->next;
            else   //每次将tail后面一个节点拿出来,放在tail后面
            {
                struct Node* item = tail->next;
                tail->next = tail->next->next;
                item->next = p->next;
                p->next = item;
            }
        return head;
    }
    int main()
    {
        Node * head;
        head = (Node *)malloc(sizeof(Node));  //head为头结点
        head ->data = NULL;
        head ->next = NULL;
        create(head);
        input(head);
        printf("
    反转后:
    ");
        head = reverseBetween(head,2,4);
        input(head);
    }

    扩展:【逆置链表】

    例子: 1 2 3 4 5 6 —— > 6 5 4 3 2 1

    方法一:

    将头结点摘下,然后从一个结点开始,依次前插入到头结点的后面【头插法建立链表】,直到最后一个结点结束为止

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct Node
    {
        int data;
        struct Node* next;
    } Node;
    void create(Node *head)
    {
        //创建链表 【尾插法】
        Node *temp,*p;
        int x;
        printf("请输入链表元素:
    ");
        scanf("%d",&x);
        p = head;   //p为尾结点
        while(x != 9999)
        {
            temp = (Node *)malloc(sizeof(Node));
            temp ->data = x;
            p ->next = temp;
            p = p ->next;
            scanf("%d",&x);
        }
        temp ->next = NULL;
    }
    void input(Node *head)
    {
        Node *p = head ->next;  //p为工作指针
        while(p != NULL)
        {
            printf("%d	",p ->data);
            p = p ->next;
        }
    }
    void reverse(Node * head)
    {
        Node *p ,*r;  //p为工作指针,r为p的后继
        p = head ->next;
        head ->next = NULL;  //将头结点的next设为NULL
        //依次将元素结点摘下
        while(p != NULL)
        {
            r = p ->next;  // 暂存p的后继
            // 将p结点插入head之后
            p ->next = head ->next;
            head ->next = p;
            p = r;
        }
    }
    int main()
    {
        Node * head;
        head = (Node *)malloc(sizeof(Node));  //head为头结点
        head ->data = NULL;
        head ->next = NULL;
        create(head);
        input(head);
        printf("
    逆置后:
    ");
        reverse(head);
        input(head);
    }
    

    方法二:

     设定三个指针:pre,p,r指向相邻的结点

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct Node
    {
        int data;
        struct Node* next;
    } Node;
    void create(Node *head)
    {
        //创建链表 【尾插法】
        Node *temp,*p;
        int x;
        printf("请输入链表元素:
    ");
        scanf("%d",&x);
        p = head;   //p为尾结点
        while(x != 9999)
        {
            temp = (Node *)malloc(sizeof(Node));
            temp ->data = x;
            p ->next = temp;
            p = p ->next;
            scanf("%d",&x);
        }
        temp ->next = NULL;
    }
    void input(Node *head)
    {
        Node *p = head ->next;  //p为工作指针
        while(p != NULL)
        {
            printf("%d	",p ->data);
            p = p ->next;
        }
    }
    void reverse(Node * head)
    {
        //将结点指针翻转
        Node *p = head ->next ,*pre,*r = p ->next;
        p ->next = NULL;   // 处理第一个结点
        while(r != NULL)    // r为空,说明p为最后一个结点
            pre = p;
            p = r;
            r = r ->next;
            p ->next = pre;   // p指向前驱pre
        }
        head ->next =p;    // 处理最后一个结点
    }
    int main()
    {
        Node * head;
        head = (Node *)malloc(sizeof(Node));  //head为头结点
        head ->data = NULL;
        head ->next = NULL;
        create(head);
        input(head);
        printf("
    逆置后:
    ");
        reverse(head);
        input(head);
    }
    

    153. 寻找旋转排序数组中的最小值

     

    方法一:二分搜索 时间复杂度【O(log2 n)】

    注意:二分查找只适用于有序的顺序表

    1、选取中间位置 mid 的数和 r 位置的数进行比较,若nums[mid] < nums[r],说明最小的数在mid之前,r = mid;反之,令 l = mid+1; 

    2、重复进行,直到 l >= r。 

    提交代码:

    int findMin(int* nums, int numsSize){
        int l = 0;
        int r = numsSize - 1;
        while(l < r)
        {
            int mid = (l + r) / 2;
            if(nums[mid] > nums[r])
            {
                l = mid + 1;
            }
            else
            {
                r = mid;
            }
        }
        return nums[l];
    }
    

    codeblocks中运行代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    void fun(int *nums,int length)
    {
        int l = 0;
        int r = length - 1;
        while(l < r)
        {
            int mid = (l + r) / 2;
            if(nums[mid] > nums[r])
            {
                l = mid + 1;
            }
            else
            {
                r = mid;
            }
        }
        printf("%d",nums[l]);
    }
    int main()
    {
        int nums[] = {3,1,2};
        int length = sizeof(nums)/sizeof(nums[0]);  //求数组长度
        fun(nums,length);
        return 0;
    }
    

    二分查找

    仅适用于有序的顺序表

    int Binary_Search(int *a,int key)
    {
    	//查找key是否在数组a中,如果在返回其位置,若不在,返回-1
    	int low = 0,high = sizeof(a)/sizeof(a[0]) - 1,mid;
    	while(low <= high)
    	{
    		mid = (low + high) / 2;  //取中间位置
    		if(a[mid] == key)
    			return mid;	//查找成功返回所在值
    		else if(a[mid] > key)
    			high = mid-1;	//从前半部分开始查找
    		else
    			low = mid+1;	//从后半部分开始查找
    	}
    	return -1;
    }
    

    215. 数组中的第K个最大元素

    方法一:先排序后找值

    排序选“快速排序”,时间复杂度为【O(log2 n)】 

    提交代码:

    int findKthLargest(int* a, int numsSize, int k)
    {
         // 先排序后查找k的位置
        int low=0,high=numsSize-1;
        while(low<high)
        {
            // 快排【从大到小】一趟排序过程
            int i=low,j=high,temp=a[i];  // temp为枢纽
            while(i<j)
            {
                while(j>i&&a[j]<=temp)
    		j--;
                if(i<j)
                    a[i]=a[j];
                while(i<j&&a[i]>=temp)
    		i++;
                if(i<j)
                    a[j]=a[i];
            }
            a[i]=temp;
    
            if(i==(k-1))   // 找到第k大的值
                return a[i];
            else if(i<(k-1))   // 若小于,则所寻元素一定在枢纽的右边
                low=i+1;
            else	// 若大于,则所寻元素一定在枢纽的左边
                high=i-1;
        }
        return a[low];
    }
    

    codeblocks中运行代码:  

    #include <stdio.h>
    #include <stdlib.h>
    
    int findKthLargest(int* a, int numsSize, int k)
    {
         // 先排序后查找k的位置
        int low=0,high=numsSize-1;
        while(low<high)
        {
            // 快排【从大到小】一趟排序过程
            int i=low,j=high,temp=a[i];  // temp为枢纽
            while(i<j)
            {
                while(j>i&&a[j]<=temp)
    		j--;
                if(i<j)
                    a[i]=a[j];
                while(i<j&&a[i]>=temp)
    		i++;
                if(i<j)
                    a[j]=a[i];
            }
            a[i]=temp;
    
            if(i==(k-1))   // 找到第k大的值
                return a[i];
            else if(i<(k-1))   // 若小于,则所寻元素一定在枢纽的右边
                low=i+1;
            else	// 若大于,则所寻元素一定在枢纽的左边
                high=i-1;
        }
        return a[low];
    }
    
    int main()
    {
        int a[6] = {1,5,9,7,4,2};
        int k;
        printf("请输入查找第K大的数:
    ");
        scanf("%d",&k);
        printf("%d",findKthLargest(a,6,k));
        return 0;
    }
    

    扩展:快排找第K⼩

    方法一:

      

    // 王道课后题代码
    int find(int a[],int low,int high,int k)
    {
        int t = a[low];  //t为枢纽
        int i = low;
        int j = high;
        while(low < high)
        {
            while(low < high && a[high] >= t)
                --high;
            a[low] = a[high];
            while(low < high && a[low] <= t)
                low++;
            a[high] = a[low];
        }
        a[low] = t;
        // 折半查找
        if(low == k)
        {
            return a[low]);
            return 0;
        }
        else if(low > k)
            return find(a,i,low - 1,k);
        else
            return find(a,low + 1,j,k - low);
    }
    

    方法二:

    #include <stdio.h>
    #include <stdlib.h>
    
    int findKthLargest(int* a, int numsSize, int k)
    {
         // 先排序后查找k的位置
        int low=0,high=numsSize-1;
        while(low<high)
        {
            // 快排【从小到大】一趟排序过程
            int i=low,j=high,temp=a[i];  // temp为枢纽
            while(i<j)
            {
                while(j>i&&a[j]>=temp)
    		j--;
                if(i<j)
                    a[i]=a[j];
                while(i<j&&a[i]<=temp)
    		i++;
                if(i<j)
                    a[j]=a[i];
            }
            a[i]=temp;
    
            if(i==(k-1))   // 找到第k小的值
                return a[i];
            else if(i<(k-1))   // 若小于,则所寻元素一定在枢纽的右边
                low=i+1;
            else	// 若大于,则所寻元素一定在枢纽的左边
                high=i-1;
        }
        return a[low];
    }
    int main()
    {
        int a[6] = {1,5,9,7,4,2};
        int k;
        printf("请输入查找第K小的数:
    ");
        scanf("%d",&k);
        printf("%d",findKthLargest(a,6,k));
        return 0;
    }
    

    242. 有效的字母异位词

      

    字母异位词

    也就是两个字符串中的相同字符的数量要对应相等。例如,s等于“anagram”,t等于“nagaram”,s和t就互为字母异位词。因为它们都包含有三个字符a,一个字符g,一个字符 m,一个字符 n,以及一个字符 r。而当 s 为 “rat”,t 为 “car”的时候,s 和 t 不互为字母异位词。

    解题思路

    一个重要的前提“假设两个字符串只包含小写字母”,小写字母一共也就26个,因此:

    知识点:哈希映射

    方法一:

      可以利用两个长度都为26的字符数组来统计每个字符串中小写字母出现的次数,然后再对比是否相等;

    提交代码:

    bool isAnagram(char * s, char * t)
    {
        //判断两个字符串长度是否相等,不相等则直接返回 false
        if(strlen(s) != strlen(t))
            return 0;
        //若相等,则初始化 26 个字母哈希表,遍历字符串 s 和 t
        int A[26] = {0},B[26] = {0};  //哈希映射
        int i;
        while(*s != '')
        {
            A[*s - 'a']++;
            B[*t - 'a']++;
            s++;
            t++;
        }
        //判断两个表是否相同
        for(i = 0; i < 26; i++)
        {
            if(A[i] != B[i])
                return 0;
        }
        return 1;
    } 

    codeblocks中运行代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int isAnagram(char * s, char * t)
    {
        //判断两个字符串长度是否相等,不相等则直接返回 false
        if(strlen(s) != strlen(t))
            return 0;
        //若相等,则初始化 26 个字母哈希表,遍历字符串 s 和 t
        int A[26] = {0},B[26] = {0};  //哈希映射
        int i;
        while(*s != '')
        {
            A[*s - 'a']++;
            B[*t - 'a']++;
            s++;
            t++;
        }
        //判断两个表是否相同
        for(i = 0; i < 26; i++)
        {
            if(A[i] != B[i])
                return 0;
        }
        return 1;
    }
    int main()
    {
        char b[] = "nagaram",a[] = "anagram";
        if(isAnagram(a,b) != 0)
            printf("True");
        else
            printf("False");
        return 0;
    }

    方法二:

    可以只利用一个长度为 26 的字符数组,将出现在字符串 s 里的字符个数加 1,而出现在字符串 t 里的字符个数减 1,最后判断每个小写字母的个数是否都为 0。

    提交代码:

    bool isAnagram(char * s, char * t)
    {
        //判断两个字符串长度是否相等,不相等则直接返回 false
        if(strlen(s) != strlen(t))
            return 0;
        //若相等,则初始化 26 个字母哈希表,遍历字符串 s 和 t
        int A[26] = {0};  //哈希映射
        int i;
        printf("%d",strlen(s));
        while(*s != '')
        {
            //s 负责在对应位置增加,t 负责在对应位置减少
            A[*s - 'a']++;
            A[*t - 'a']--;
            s++;
            t++;
        }
        //如果哈希表的值都为 0,则二者是字母异位词
        for(i = 0; i < 26; i++)
        {
            if(A[i] != 0)
                return 0;
        }
        return 1;
    }

    codeblocks中运行代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int isAnagram(char * s, char * t)
    {
        //判断两个字符串长度是否相等,不相等则直接返回 false
        if(strlen(s) != strlen(t))
            return 0;
        //若相等,则初始化 26 个字母哈希表,遍历字符串 s 和 t
        int A[26] = {0};  //哈希映射
        int i;
        while(*s != ')
        {
            //s 负责在对应位置增加,t 负责在对应位置减少
            A[*s - 'a']++;
            A[*t - 'a']--;
            s++;
            t++;
        }
        //如果哈希表的值都为 0,则二者是字母异位词
        for(i = 0; i < 26; i++)
        {
            if(A[i] != 0)
                return 0;
        }
        return 1;
    }
    int main()
    {
        char b[] = "nagaram",a[] = "anagram";
        if(isAnagram(a,b) != 0)
            printf("True");
        else
            printf("False");
        return 0;
    }

    25. K 个一组翻转链表

      

    解题思路

    参考:https://leetcode-cn.com/problems/reverse-nodes-in-k-group/solution/kge-yi-zu-fan-zhuan-lian-biao-by-powcai/

    这道题考察了两个知识点:

      对链表翻转算法是否熟悉

      对递归算法的理解是否清晰

    在翻转链表的时候,可以借助三个指针:prev、curr、next,分别代表前一个节点、当前节点和下一个节点,实现过程如下所示:

    递归方法1:

    • 1、找到待翻转的k个节点(注意:若剩余数量小于k的话,则不需要反转,因此直接返回待翻转部分的头结点即可)。
    • 2、对其进行翻转。并返回翻转后的头结点(注意:翻转为左闭又开区间,所以本轮操作的尾结点其实就是下一轮操作的头结点)。
    • 3、对下一轮k个节点也进行翻转操作。
    • 4、将上一轮翻转后的尾结点指向下一轮翻转后的头节点,即将每一轮翻转的k的节点连接起来。

    提交代码:

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     struct ListNode *next;
     * };
     */
    
    
    struct ListNode* reverse(struct ListNode *head,struct ListNode *tail)
    {
        struct ListNode* pre = NULL;
        struct ListNode* next = NULL;
        while(head != tail)
        {
            next = head ->next;
            head ->next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }
    struct ListNode*reverseKGroup(struct ListNode* head, int k)
    {
        if(head == NULL || head ->next == NULL)
            return head;
        struct ListNode *newHead,*tail = head;
        int i;
        for(i = 0;i < k;i++)
        {
            //剩余数量小于k的话,不需要反转
            if(tail == NULL)
                return head;
            tail = tail ->next;
        }
        //反转前K个元素
        newHead = reverse(head,tail);
        //下一轮的开始的地方就是tail
        head ->next = reverseKGroup(tail,k);
    
        return newHead;
    }

    codeblocks中运行代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct slist
    {
        int data;
        struct slist *next;
    };
    struct slist *reverse(struct slist *head,struct slist *tail)
    {
        struct slist *pre = NULL;
        struct slist *next = NULL;
        while(head != tail)
        {
            next = head ->next;
            head ->next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }
    struct slist *reverseKGroup(struct slist* head, int k)
    {
        if(head == NULL || head ->next == NULL)
            return head;
        struct slist *newHead,*tail = head;
        int i;
        for(i = 0;i < k;i++)
        {
            //剩余数量小于k的话,不需要反转
            if(tail == NULL)
                return head;
            tail = tail ->next;
        }
        //反转前K个元素
        newHead = reverse(head,tail);
        //下一轮的开始的地方就是tail
        head ->next = reverseKGroup(tail,k);
    
        return newHead;
    }
    
    void input(struct slist *head)
    {
        struct slist *p = head ->next; //p是工作指针
        while(p != NULL)
        {
            printf("%d	",p ->data);
            p = p ->next;
        }
    }
    void create(struct slist *head)
    {
        //尾插法建立单链表
        struct slist *r,*temp; //r是尾指针,temp是临时结点
        int i,x;
        r = head;
        printf("请输入元素:
    ");
        scanf("%d",&x);
        while(x != 9999)
        {
            temp = (struct slist *)malloc(sizeof(struct slist));
            temp ->data = x;
            temp ->next = r ->next;
            r ->next = temp;
            r = temp;
            scanf("%d",&x);
        }
    }
    int main()
    {
        struct slist *head;//head是头结点
    
        head = (struct slist *)malloc(sizeof(struct slist));
        head ->next = NULL;
        create(head);
        input(head);
    
        int k;
        printf("
    请输入K:");
        scanf("%d",&k);
        head ->next= reverseKGroup(head ->next,k);
        input(head);
    
        return 0;
    }
    

    递归方法2:

     

    提交代码:

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     struct ListNode *next;
     * };
     */
    
    
    struct ListNode *reverseKGroup(struct ListNode* head, int k)
    {
        struct ListNode *cur = head;
        int count = 0;
        // 找到待翻转的k个节点
        while(cur != NULL && count != k)
        {
            cur = cur ->next;
            count++;
        }
        if(count == k)
        {
            cur = reverseKGroup(cur,k);
            while(count != 0)
            {
                count--;
                struct ListNode *tmp = head ->next;
                head ->next = cur;
                cur = head;
                head = tmp;
            }
            head = cur;
        }
        //若剩余数量小于k的话,则不需要反转,因此直接返回待翻转部分的头结点即可
        return head;  //head为头指针
    }

    codeblocks中运行代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct slist
    {
        int data;
        struct slist *next;
    };
    struct slist *reverseKGroup(struct slist* head, int k)
    {
        struct slist *cur = head;
        int count = 0;
        // 找到待翻转的k个节点
        while(cur != NULL && count != k)
        {
            cur = cur ->next;
            count++;
        }
        if(count == k)
        {
            cur = reverseKGroup(cur,k);
            while(count != 0)
            {
                count--;
                struct slist *tmp = head ->next;
                head ->next = cur;
                cur = head;
                head = tmp;
            }
            head = cur;
        }
        //若剩余数量小于k的话,则不需要反转,因此直接返回待翻转部分的头结点即可
        return head;  //head为头指针
    }
    
    void input(struct slist *head)
    {
        struct slist *p = head ->next; //p是工作指针
        while(p != NULL)
        {
            printf("%d	",p ->data);
            p = p ->next;
        }
    }
    void create(struct slist *head)
    {
        //尾插法建立单链表
        struct slist *r,*temp; //r是尾指针,temp是临时结点
        int i,x;
        r = head;
        printf("请输入元素:
    ");
        scanf("%d",&x);
        while(x != 9999)
        {
            temp = (struct slist *)malloc(sizeof(struct slist));
            temp ->data = x;
            temp ->next = r ->next;
            r ->next = temp;
            r = temp;
            scanf("%d",&x);
        }
    }
    int main()
    {
        struct slist *head;//head是头结点
    
        head = (struct slist *)malloc(sizeof(struct slist));
        head ->next = NULL;
        create(head);
        input(head);
    
        int k;
        printf("
    请输入K:");
        scanf("%d",&k);
        head ->next= reverseKGroup(head ->next,k);
        input(head);
    
        return 0;
    } 

    20. 有效的括号

     

    解题思路

    利用一个栈,不断地往里压左括号,一旦遇上了一个右括号,我们就把栈顶的左括号弹出来,表示这是一个合法的组合,以此类推,直到最后判断栈里还有没有左括号剩余。

    提交代码:

    bool isValid(char * p)
    {
        int len=strlen(p);
        if(len == 1)
            return 0;
        int top=1,i;
        char s[9999];  //堆栈存储
        for(i=0; i<len; i++)
        {
            switch(p[i])
            {
            case '(':
            case '[':
            case '{':
                s[top++]=p[i];
                break;
            case ')':
                if(s[top-1]=='(')
                    top--;
                else return 0;
                break;
            case ']':
                if(s[top-1]=='[')
                    top--;
                else return 0;
                break;
            case '}':
                if(s[top-1]=='{')
                    top--;
                else return 0;
                break;
            }
        }
        if(top==1)
            return 1;  //输出1表示匹配成功
        else
            return 0;   //输出0表示匹配失败
    }
    

    codeblocks中运行代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define Max 20
    #define Stack char
    int isValid(char * p)
    {
        int len=strlen(p);
    
        if(len == 1)
            return 0;
        int top=1,i;
        Stack s[Max];  //数组模拟堆栈存储
        for(i=0; i<len; i++)
        {
            switch(p[i])
            {
            case '(':
            case '[':
            case '{':
                s[top++]=p[i];  //进栈
                break;
            case ')':
                if(s[top-1]=='(')
                    top--;  //出栈
                else return 0;
                break;
            case ']':
                if(s[top-1]=='[')
                    top--;  //出栈
                else return 0;
                break;
            case '}':
                if(s[top-1]=='{')
                    top--;//出栈
                else return 0;
                break;
            }
        }
        if(top==0)
            return 1;  //输出1表示匹配成功
        else
            return 0;   //输出0表示匹配失败
    }
    int main()
    {
        char s[Max];
        printf("请输入括号:");
        scanf("%s",s);
    
        if(isValid(s) != 0)
            printf("true");
        else
            printf("false");
        return 0;
    }

    692. 前K个高频单词

      

    方法一 使用HashMap

    1、建一个 HashMap <key = 单词,value = 出现频率>,遍历整个数组,相应的把这个单词的出现次数 + 1.

    这一步时间复杂度是 O(n).

    2、用 size = k 的 minHeap 来存放结果,定义好题目中规定的比较顺序
      a. 首先按照出现的频率排序;
      b. 频率相同时,按字母顺序。

    3、遍历这个 map,如果
      a. minHeap 里面的单词数还不到 k 个的时候就加进去;
      b. 或者遇到更高频的单词就把它替换掉。

    时空复杂度分析:

    第一步是 O(n),第三步是 nlog(k),所以加在一起时间复杂度是 O(nlogk).

    用了一个额外的 heap 和 map,空间复杂度是 O(n).

    提交代码:

    class Func {
        public List<String> topKFrequent(String[] words, int k) {
            // Step 1
            Map<String, Integer> map = new HashMap<>(); // 建一个 HashMap <key = 单词,value = 出现频率>
            //遍历整个数组,相应的把这个单词的出现次数 + 1
            for (String word : words) {
                Integer count = map.getOrDefault(word, 0);//次数初始化为0
                count++;
                map.put(word, count);
            }
            
            // Step 2 用 size = k 的 minHeap 来存放结果
            PriorityQueue<Map.Entry<String, Integer>> minHeap = new PriorityQueue<>(k+1, new Comparator<Map.Entry<String, Integer>>() {
                @Override
                public int compare(Map.Entry<String, Integer> e1, Map.Entry<String, Integer> e2) {
                    if(e1.getValue() == e2.getValue()) {  
                    	//频率相同时,按字母顺序
                        return e2.getKey().compareTo(e1.getKey());
                    }
                    //首先按照出现的频率排序
                    return e1.getValue().compareTo(e2.getValue());
                }
            });
            
            // Step 3
            List<String> res = new ArrayList<>();
            //遍历这个 map
            for(Map.Entry<String, Integer> entry : map.entrySet()) {
                minHeap.offer(entry);
                if(minHeap.size() > k) {
                    minHeap.poll();
                }
            }
            while(!minHeap.isEmpty()) {
                res.add(minHeap.poll().getKey());
            }
            Collections.reverse(res);
            return res;
        }
    }
    

    eclipse中运行代码:

    package top;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.PriorityQueue;
    
    class Func {
        public List<String> topKFrequent(String[] words, int k) {
            // Step 1
            Map<String, Integer> map = new HashMap<>(); // 建一个 HashMap <key = 单词,value = 出现频率>
            //遍历整个数组,相应的把这个单词的出现次数 + 1
            for (String word : words) {
                Integer count = map.getOrDefault(word, 0);//次数初始化为0
                count++;
                map.put(word, count);
            }
            
            // Step 2 用 size = k 的 minHeap 来存放结果
            PriorityQueue<Map.Entry<String, Integer>> minHeap = new PriorityQueue<>(k+1, new Comparator<Map.Entry<String, Integer>>() {
                @Override
                public int compare(Map.Entry<String, Integer> e1, Map.Entry<String, Integer> e2) {
                    if(e1.getValue() == e2.getValue()) {  
                    	//频率相同时,按字母顺序
                        return e2.getKey().compareTo(e1.getKey());
                    }
                    //首先按照出现的频率排序
                    return e1.getValue().compareTo(e2.getValue());
                }
            });
            
            // Step 3
            List<String> res = new ArrayList<>();
            //遍历这个 map
            for(Map.Entry<String, Integer> entry : map.entrySet()) {
                minHeap.offer(entry);
                if(minHeap.size() > k) {
                    minHeap.poll();
                }
            }
            while(!minHeap.isEmpty()) {
                res.add(minHeap.poll().getKey());
            }
            Collections.reverse(res);
            return res;
        }
    }
    public class Solution{
    	public static void main(String[] args)
    	{
    		Func f = new Func();
    		String str[] = {"the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"};
    		List<String> res = new ArrayList<>();
    		int k = 4;
    		res = f.topKFrequent(str, k);
    		System.out.println(res);
    	}
    }
    

    方法二:C语言使用uthash 

    提交代码:

    typedef struct {
        char* key;
        int count;
        UT_hash_handle hh;
    } WordHash;
    
    typedef struct {
        WordHash* wh;
    } WordsCounter;
    
    void WordsCounterAddWord(WordsCounter* obj, char* word)
    {
        WordHash* cur;
        HASH_FIND(hh, obj->wh, word, strlen(word), cur);
        if (cur == NULL) {
            cur = malloc(sizeof(WordHash));
            cur->key = malloc(strlen(word) + 1);
            strcpy(cur->key, word);
            cur->count = 0;
            HASH_ADD_KEYPTR(hh, obj->wh, cur->key, strlen(cur->key), cur);
        }
        cur->count++;
        return;
    }
    
    WordsCounter* WordsCounterCreate(char** words, int wordsSize)
    {
        WordsCounter* obj = malloc(sizeof(WordsCounter));
        obj->wh = NULL;
        for (int i = 0; i < wordsSize; i++) {
            WordsCounterAddWord(obj, words[i]);
        }
        return obj;
    }
    
    int WordsCounterCompare(WordHash* a, WordHash* b) 
    {
        if (a->count < b->count) {
            return 1;
        }
    
        if (a->count > b->count) {
            return -1;
        }
        return strcmp(a->key, b->key);
    }
    
    char** WordsCounterTopKFrequent(WordsCounter* obj, int k, int* returnSize)
    {
        char** topK = malloc(sizeof(char*) * k);
        int topKIndex = 0;
        WordHash *wcc;
        WordHash *wct;
        HASH_ITER(hh, obj->wh, wcc,wct) {
            HASH_DEL(obj->wh, wcc);
            if (topKIndex < k) {
                topK[topKIndex++] = wcc->key;
            } else {
                free(wcc->key);
            }
            free(wcc);
        }
        *returnSize = k;
        return topK;
    }
    
    char** topKFrequent(char** words, int wordsSize, int k, int* returnSize)
    {
        WordsCounter* obj =  WordsCounterCreate(words, wordsSize);
        HASH_SORT(obj->wh, WordsCounterCompare);
        char** ans = WordsCounterTopKFrequent(obj, k, returnSize);
        return ans;
    }
    

    767. 重构字符串

    提交代码

    class Solution{
        public String reorganizeString(String S) {
    
            int[] chs = new int[26];
    
            //记录字符出现的最长长度
            int maxLen = 0;
            for(char ch : S.toCharArray()){
                maxLen = Math.max(maxLen, ++chs[ch - 'a']);
            }
    
            int len = S.length();
            /*
                如果长度为奇数个:那么极端情况是 aaa bc,能够组成 abaca,即最长字符的长度不能超过 len / 2 向上取整,即 不能超过 (len + 1) / 2
                如果长度为偶数个,那么极端情况是 aaa bcd,能够组成 abacad,即最长字符的长度不能超过 len / 2,即 6 / 2
    
                综上
                总长度为 奇数,最长字符长度不能超过 长度的一半(向上取整)
                总长度为 偶数,最长字符串长度不能超过 长度的一半
    
                偶数:
                len / 2 == (len + 1) / 2
                如果 len = 8,那么结果为 4
                len / 2 + 1
                如果 len = 8,那么结果为 5
    
                奇数:
                len / 2
                如果 len == 7,那么结果为 3
                len / 2 + 1 == (len + 1) / 2 (向上取整)
                如果 len == 7,那么结果为 4
    
                为了方便,我们可以写成 (len + 1) / 2
            */
            if(maxLen > (len + 1) / 2){
                return "";
            }
    
            /*
            索引位置从 0 开始算
    
            aaa bb ccc
            0 1 2 3 4 5 6 7
            b a c a c a c b
    
            aaaa b ccc
            0 1 2 3 4 5 6 7
            a b a c a c a c
    
            aaaa bbbb
            0 1 2 3 4 5 6 7
            a b a b a b a b
    
            对于总长度为奇数,如果 字符 长度为 (len + 1) / 2,即向上取整,那么这个字符首先需要放在首尾,即偶数位(因为需要夹紧其他字符,比如 ababa)
            对于总长度为偶数,没有什么特别要求,因为无论怎么整,字符的长度最长只能为总长度的一半,那么只要间隔存放,奇数位偶数位没差别(比如 abababab)
            
            综上,我们只需要处理 总长度奇数 的情况即可,
            这里我们先写出放在奇数位的条件,chs[i] < (len + 1) / 2
            但这里写的话会有问题,因为如果总长度是 偶数的话, 如果存在这么一个情况 aaaabbbb,
            那么 每个字符出现的次数都是 4,占了总长度一半,如果写成 chs[i] < (len + 1) / 2,(我们上面说了 对于偶数长度,len / 2 == (len + 1) / 2),
            即 只有在 chs[i] < len / 2 的情况才能放在奇数
            那么对于 这个 a 来说,由于 chs[i] < len / 2 不成立,因此不会放在奇数,都放在偶数位,然后下一个 b ,同样也不会放在奇数,导致奇数位不会被放置,结果错误
            
            因此,我们需要修改成既能限制 奇数长度的放置,又能不影响 偶数长度的放置
            因为对于 奇数长度来说,len / 2 + 1 == (len + 1) / 2,而对于偶数长度来说是不一样的,因此我们采用 len / 2 + 1 这个写法,
            这样的话,对于 a 来说,就是 4 < 5 了,a 放置完奇数位,然后奇数位越界,因此 b 会 放置在偶数位,那么结果就是 abababab
            */
            char[] res = new char[len];
            int even = 0;
            int odd = 1;
            for(int i = 0; i < 26; i++){
                //元素个数不为 0 并且 长度 小于 len / 2 + 1,并且奇数位下标还没越界,那么将元素放在奇数位
                while(chs[i] > 0 && chs[i] < len / 2 + 1 && odd < len){
                    res[odd] = (char)(i + 'a');
                    chs[i]--;
                    odd += 2;
                }
                //当 odd 越界了,或者 长度等于 len / 2 + 1,那么就会放在偶数位
                while(chs[i] > 0){
                    res[even] = (char)(i + 'a');
                    chs[i]--;
                    even += 2;
                }
            }
            return new String(res);
        }
    }

    eclipse运行:

    package top;
    
    import java.util.ArrayList;
    import java.util.List;
    
    class Func {
        public String reorganizeString(String S) {
    
            int[] chs = new int[26];
    
            //记录字符出现的最长长度
            int maxLen = 0;
            for(char ch : S.toCharArray()){
                maxLen = Math.max(maxLen, ++chs[ch - 'a']);
            }
    
            int len = S.length();
            /*
                如果长度为奇数个:那么极端情况是 aaa bc,能够组成 abaca,即最长字符的长度不能超过 len / 2 向上取整,即 不能超过 (len + 1) / 2
                如果长度为偶数个,那么极端情况是 aaa bcd,能够组成 abacad,即最长字符的长度不能超过 len / 2,即 6 / 2
    
                综上
                总长度为 奇数,最长字符长度不能超过 长度的一半(向上取整)
                总长度为 偶数,最长字符串长度不能超过 长度的一半
    
                偶数:
                len / 2 == (len + 1) / 2
                如果 len = 8,那么结果为 4
                len / 2 + 1
                如果 len = 8,那么结果为 5
    
                奇数:
                len / 2
                如果 len == 7,那么结果为 3
                len / 2 + 1 == (len + 1) / 2 (向上取整)
                如果 len == 7,那么结果为 4
    
                为了方便,我们可以写成 (len + 1) / 2
            */
            if(maxLen > (len + 1) / 2){
                return "";
            }
    
            /*
            索引位置从 0 开始算
    
            aaa bb ccc
            0 1 2 3 4 5 6 7
            b a c a c a c b
    
            aaaa b ccc
            0 1 2 3 4 5 6 7
            a b a c a c a c
    
            aaaa bbbb
            0 1 2 3 4 5 6 7
            a b a b a b a b
    
            对于总长度为奇数,如果 字符 长度为 (len + 1) / 2,即向上取整,那么这个字符首先需要放在首尾,即偶数位(因为需要夹紧其他字符,比如 ababa)
            对于总长度为偶数,没有什么特别要求,因为无论怎么整,字符的长度最长只能为总长度的一半,那么只要间隔存放,奇数位偶数位没差别(比如 abababab)
            
            综上,我们只需要处理 总长度奇数 的情况即可,
            这里我们先写出放在奇数位的条件,chs[i] < (len + 1) / 2
            但这里写的话会有问题,因为如果总长度是 偶数的话, 如果存在这么一个情况 aaaabbbb,
            那么 每个字符出现的次数都是 4,占了总长度一半,如果写成 chs[i] < (len + 1) / 2,(我们上面说了 对于偶数长度,len / 2 == (len + 1) / 2),
            即 只有在 chs[i] < len / 2 的情况才能放在奇数
            那么对于 这个 a 来说,由于 chs[i] < len / 2 不成立,因此不会放在奇数,都放在偶数位,然后下一个 b ,同样也不会放在奇数,导致奇数位不会被放置,结果错误
            
            因此,我们需要修改成既能限制 奇数长度的放置,又能不影响 偶数长度的放置
            因为对于 奇数长度来说,len / 2 + 1 == (len + 1) / 2,而对于偶数长度来说是不一样的,因此我们采用 len / 2 + 1 这个写法,
            这样的话,对于 a 来说,就是 4 < 5 了,a 放置完奇数位,然后奇数位越界,因此 b 会 放置在偶数位,那么结果就是 abababab
            */
            char[] res = new char[len];
            int even = 0;
            int odd = 1;
            for(int i = 0; i < 26; i++){
                //元素个数不为 0 并且 长度 小于 len / 2 + 1,并且奇数位下标还没越界,那么将元素放在奇数位
                while(chs[i] > 0 && chs[i] < len / 2 + 1 && odd < len){
                    res[odd] = (char)(i + 'a');
                    chs[i]--;
                    odd += 2;
                }
                //当 odd 越界了,或者 长度等于 len / 2 + 1,那么就会放在偶数位
                while(chs[i] > 0){
                    res[even] = (char)(i + 'a');
                    chs[i]--;
                    even += 2;
                }
            }
            return new String(res);
        }
    }
    public class Solution{
        public static void main(String[] args)
        {
            Func f = new Func();
            String str = "aacab";
            String r = f.reorganizeString(str);
            System.out.println(r);
        }
    }
    

      

      

      

      

      

      

  • 相关阅读:
    【android tools】内存、网络、界面性能响应优化的工具
    mysql命令(数据库备份与恢复)
    mysql中一些简单但是新手容易犯的错误
    Mysql bench执行sql语句批量操作数据所遇到的问题
    Excel “此单元格中的数字为文本格式,或者其前面有撇号” 设成数字格式
    VC程序异常中断的原因
    vs dump调试
    winsock.h与winsock2.h出现重定义或不同的链接
    QT中QString与string的转化,解决中文乱码问题
    线程中CreateEvent和SetEvent及WaitForSingleObject的用法
  • 原文地址:https://www.cnblogs.com/pam-sh/p/12757978.html
Copyright © 2020-2023  润新知