在本篇中,将尽力使用多种解法,来达到一题多练的效果。
1:
1.原题链接:
238. 除自身以外数组的乘积 - 力扣(LeetCode)
这道题首先一眼肯定想到拿整体的积除以当前元素,将结果作为ans,但是一来题目说了不要用除法,二来除法要注意零元素。(虽然实际上是可以用除法来写的)
前缀:
所以我们就要换思路来写,要除了当前元素外的所有元素的积,也就是前面元素的积乘后面元素的积,这里就能想到前缀和后缀两点,所以首先可以先找出来每个元素之前的元素的积,存入一个数组,之后再找出该元素后面的元素的乘积,与前缀相乘即可。
所以这里我们就先动态分配一个数组
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* productExceptSelf(int* nums, int numsSize, int* returnSize){
int i=0, pro = 1;
int *ans = malloc(numsSize * sizeof(int));
for (i = 0; i < numsSize; i++) {
ans[i] = pro;
pro = pro * nums[i];
}
pro = 1;
for (i = numsSize - 1; i >=0 ; i--) {
ans[i] = ans[i] * pro;
pro = nums[i]*pro;
}
*returnSize = numsSize;
return res;
}
这里的pro是中间变量,我们将这个中间变量不断与当前元素相乘,就得到了前缀。
注意:
这里先将pro赋值给ans是因为要求的是:要除去当前元素的前缀和。所以先赋值之后再进行操作。
接下来,pro置为1,倒着遍历数组求出后缀并于ans相乘就可以得到答案。
下面是取巧的做法,
使用除法,先遍历一遍找出所有0,将非0项求积。
再遍历分四种情况:
1.0的个数大于1,那么剩下肯定是0
2.0个数为1,判断当前元素是否为0,是0,则就是上面所求的积,反之则是0
3.没有0,那么直接就是计算出的积除以当前元素。
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* productExceptSelf(int* nums, int numsSize, int* returnSize) {
int* ans = (int*)malloc(sizeof(int) * numsSize);
memset(ans,1,sizeof(int)*numsSize);
long long sum = 1;
long long zero = 0;
for (int i = 0; i < numsSize; i++) {
if (nums[i] != 0)
sum *= nums[i];
else
zero++;
}
for (int i = 0; i < numsSize; i++) {
if (zero > 1) {
ans[i] = 0;
} else if (zero == 1 && nums[i] == 0) {
ans[i] = sum;
} else if (zero == 1 && nums[i] != 0) {
ans[i] = 0;
} else if (zero == 0) {
ans[i] = sum / nums[i];
}
}
*returnSize = numsSize;
return ans;
}
2:
2.原题链接:
394. 字符串解码 - 力扣(LeetCode)
本题难点在于括号内嵌套括号,需要从内向外生成与拼接字符串,这与栈的先入后出特性对应。
我的思路是,创建一个栈保存字符,创建一个数组保存数字。
遇到左括号,则索引位置入栈(输出串的尾指针);
遇到“ ] ”时,从索引栈里弹出栈顶元素——就是最近的与之闭合的左括号,这部分内容是要被复制的,再找到数字数组最新元素元素,是几就把这部分内容复制几遍。
然后塞回栈中,等待之后使用。
这里的难点在于:要处理的太多了,很容易脑子就乱了,稍微简化一点的话,可以将数字数组删除,直接数字入栈,找到‘[’,他前面就是数字,这个时候,直接取出这个数字就可以了
char* decodeString(char* s) {
int len = (int)strlen(s);
int stackSize = 50;
char* stack = (char*)malloc(stackSize * sizeof(char));
int top = -1;
for (int i = 0; i < len; i++) {
char c = s[i];
if (c != ']') {
if (top == stackSize - 1) {
stack = realloc(stack, (stackSize += 50) * sizeof(char));
}
stack[++top] = c;
} else {
int tempSize = 10;
char* tempStack = (char*)malloc(tempSize * sizeof(char));
int tempTop = -1;
while (stack[top] != '[') {
if (tempTop == tempSize - 1) {
tempStack =
realloc(tempStack, (tempSize += 10) * sizeof(char));
}
tempStack[++tempTop] = stack[top];
top--;
}
char* cpyStr = (char*)malloc((tempTop + 2) * sizeof(char));
for (int i = tempTop; i >= 0; i--) {
cpyStr[tempTop - i] = tempStack[i];
}
cpyStr[++tempTop] = '\0';
top--;
int cpyNum = 0;
int j = 0;
while (top >= 0 && stack[top] >= '0' && stack[top] <= '9') {
cpyNum += (stack[top] - '0') * pow(10, j);
top--;
j++;
}
for (int i = 0; i < cpyNum; i++) {
for (int j = 0; j < strlen(cpyStr); j++) {
if (top == stackSize - 1) {
stack =
realloc(stack, (stackSize += 50) * sizeof(char));
}
stack[++top] = cpyStr[j];
}
}
free(tempStack);
}
}
char* result = realloc(stack, (top + 2) * sizeof(char));
result[++top] = '\0';
return result;
}
3:
3.原题链接:
73. 矩阵置零 - 力扣(LeetCode)
如果遍历一遍该数组边遍历边置零的话,就会导致之前置零的影响之后的。
所以我们先遍历一遍,将每一个地方是0的元素都标记为1,之后再遍历hash数组,将原来为1的地方进行操作变成0就行了。
这里的
O(mn)的额外空间就是二维hash数组
O(m+n)就是用两个数组
一旦元素为0,就将该位置的下标地方的a,b数组都置为1,
这样遍历的时候只要两个同时为1就可以保证这个元素要改。
void setZeroes(int** matrix, int matrixSize, int* matrixColSize) {
int h = matrixColSize[0];
int l = matrixSize;
int a[l][h];
for (int i = 0; i < l; i++) {
for (int j = 0; j < h; j++) {
if (matrix[i][j] == 0) {
a[i][j] = 1;
} else {
a[i][j] = 0;
}
}
}
for (int i = 0; i < l; i++) {
for (int j = 0; j < h; j++) {
if (a[i][j] == 1) {
for (int k = 0; k < l; k++) {
matrix[k][j] = 0;
}
for (int k = 0; k < h; k++) {
matrix[i][k] = 0;
}
}
}
}
}
4:
4.原题链接:
23. 合并 K 个升序链表 - 力扣(LeetCode)
这是合并两个升序链表这道题的plus版,合并两个的时候,我们怎么处理呢?
用归并排序的思路来:
递归:
如果 l1 或者 l2 一开始就是空链表 ,那么没有任何操作需要合并,所以我们只需要返回非空链表。>否则,我们要判断 l1 和 l2 哪一个链表的头节点的值更小,然后递归地决定下一个添加到结果里的>节点。如果两个链表有一个为空,递归结束。
迭代:
1.初始化: 伪头节点 dum ,节点 cur 指向 dum 。
2.循环合并:当或2为空时跳出。
a.当 l.val < lz.val 时: cur 的后继节点指定为,并向前走一步
b.当 l.val > z.val 时:
cur 的后继节点指定为 2,并2 向前走 步
c.节点 cur 向前走一步,即 cur = cur.next 。
3.合并剩余尾部: 跳出时有两种情况,即,为空或2为空
a.若 l 头null : 将 添加至节点 cur 之后
b.否则: 将1。添加至节点 cur 之后
4.返回值:合并链表在伪头节点 dum 之后,因此返回 dum.neat 即可。
那么放大到n个的时候我们完全可以一个一个来,每一次都将list[i]与list[0]链接,那么之后呢链接的新链表又是list[0],再与之后的链接,我们就可以用同样的思路完成该题。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
if(list1 == NULL){
return list2;
}
if(list2 == NULL){
return list1;
}
struct ListNode* ans;
if(list1->val <= list2->val){
ans = list1;
ans->next = mergeTwoLists(list1->next,list2);
}
else{
ans = list2;
ans->next = mergeTwoLists(list1,list2->next);
}
return ans;
}
struct ListNode* mergeKLists(struct ListNode** lists, int listsSize) {
if(!listsSize) return NULL;
struct ListNode* head = NULL;
for(int i = 0; i < listsSize; i++){
head = mergeTwoLists(head,lists[i]);
}
return head;
}
取巧法:
接下来,是另一个思路,说是升序链表,又没说必须操作链表,那我们就将所有元素取出存入数组,排序之后,再存入一个新链表不就可以了吗。逃课代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int cmp(const void* a, const void* b) { return *(int*)a - *(int*)b; }
struct ListNode* mergeKLists(struct ListNode** lists, int listsSize) {
int a[10000];
int j = 0;
for (int i = 0; i < listsSize; i++) {
struct ListNode* move = lists[i];
while (move) {
a[j++] = move->val;
move = move->next;
}
}
qsort(a, j, sizeof(int), cmp);
struct ListNode* q = (struct ListNode*)malloc(sizeof(struct ListNode));
q->next = NULL;
struct ListNode* dong = q;
for (int i = 0; i < j ; i++) {
struct ListNode* new =
(struct ListNode*)malloc(sizeof(struct ListNode));
new->val = a[i];
new->next = NULL;
dong->next = new;
dong = dong->next;
}
return q->next;
}
5:
5.原题链接:
232. 用栈实现队列 - 力扣(LeetCode)
学队列的时候必练的题目:
我们用两个栈模拟一个队列,入队操作的时候进入in,出队时,先将in的出栈到out中,再从out出栈就可以完成出队操作。
typedef struct a {
int stcin[10], stcout[10];
int stcintop, stcouttop;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* q = (MyQueue*)malloc(sizeof(MyQueue));
q->stcintop = 0;
q->stcouttop = 0;
return q;
}
void myQueuePush(MyQueue* obj, int x) { obj->stcin[obj->stcintop++] = x; }
int myQueuePop(MyQueue* obj) {
if (obj->stcouttop == 0) {
while (obj->stcintop > 0) {
obj->stcout[obj->stcouttop++] = obj->stcin[--obj->stcintop];
}
}
int ans = obj->stcout[--obj->stcouttop];
while (obj->stcouttop > 0) {
obj->stcin[obj->stcintop++] = obj->stcout[--obj->stcouttop];
}
return ans;
}
int myQueuePeek(MyQueue* obj) {
return obj->stcin[0]; }
bool myQueueEmpty(MyQueue* obj) {
return obj->stcintop == 0 && obj->stcouttop == 0;
}
void myQueueFree(MyQueue* obj) {
obj->stcintop = 0;
obj->stcouttop = 0;
}
/**
* Your MyQueue struct will be instantiated and called as such:
* MyQueue* obj = myQueueCreate();
* myQueuePush(obj, x);
* int param_2 = myQueuePop(obj);
* int param_3 = myQueuePeek(obj);
* bool param_4 = myQueueEmpty(obj);
* myQueueFree(obj);
*/
6:
6.原题链接:
61. 旋转链表 - 力扣(LeetCode)
首先是常规的模拟法
假设链表的长度为n,为了将链表每个节点向右移动 k 个位置,我们只需要将链表的后 k % n个节点移动到链表的最前面,然后将链表的后k % n个节点和前 n - k个节点连接到一块即可。
具体过程如下:
1、首先遍历整个链表,求出链表的长度n,并找出链表的尾节点tail。
2、由于k可能很大,所以我们令 k = k % n,然后再次从头节点head开始遍历,找到第n - k个节点p,那么1 ~ p是链表的前 n - k个节点,p+1 ~ n是链表的后k个节点。
3、接下来就是依次执行 tail->next = head,head = p->next,p->next = nullptr,将链表的后k个节点和前 n - k个节点拼接到一块,并让head指向新的头节点(p->next),新的尾节点即p节点的next指针指向null。
4、最后返回链表的新的头节点head。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* rotateRight(struct ListNode* head, int k) {
if (!head || !k) return head;
int n = 0; // 链表的长度
struct ListNode* tail; // 尾节点
struct ListNode* p = head;
while (p) {
tail = p;
p = p->next;
n++;
}
k %= n;
p = head;
for (int i = 0; i < n - k - 1; i++) p = p->next; // 找到链表的第n-k个节点
tail->next = head;
head = p->next;
p->next = NULL;
return head; // 返回新的头节点
}
接下来是取巧法:
我们看到这种,那继续之前合并k个链表的思路,只管开头和结尾,将元素取出组成数组,然后将整个数组复制一份接到另一个数组之后,接下来,只需要将第一个元素更新为k%n个,那就是将链表移动k个之后的链表的样子啦,我们再将数值赋值进新链表就可以完美解决。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* rotateRight(struct ListNode* head, int k) {
if (head == NULL || head->next == NULL)
return head;
if (k == 0)
return head;
int a[501];
int j = 0;
struct ListNode* move = head;
while (move) {
a[j++] = move->val;
move = move->next;
}
int b[j * 2];
for (int i = 0; i < j; i++) {
b[i + j] = a[i];
b[i] = a[i];
}
for (int i = 0; i < j; i++) {
printf("%d", a[i]);
}
struct ListNode* q = (struct ListNode*)malloc(sizeof(struct ListNode));
q->next = NULL;
struct ListNode* dong = q;
for (int i = (j - k % j); i < j+(j - k % j) ; i++) {
struct ListNode* new =
(struct ListNode*)malloc(sizeof(struct ListNode));
new->val = b[i];
new->next = NULL;
dong->next = new;
dong = dong->next;
}
return q->next;
}