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 个一组翻转链表
解题思路
这道题考察了两个知识点:
对链表翻转算法是否熟悉
对递归算法的理解是否清晰
在翻转链表的时候,可以借助三个指针: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); } }