目录
一.力扣27-移除元素
1.题目
2.思考
3.代码
3.代码2
二.力扣88-合并两个有序数组
1.题目
2.想法:
3.代码
三.剑指offer交换奇偶数的位置
1.题目
2. 分析
3.代码
四.剑指offer56题
1.tip:
2.代码:
五.剑指offer---计算一个整数在计算机存储的二进制位中1的个数
代码1
代码2
六.力扣-消失的数字
思路
七.循环右移
八.力扣206-反转链表
1.题目
2. 代码
1--头插
3.递归---整个链表反转
3.1递归--前n个结点反转
3.2反转部分链表—递归92
3.2.1-循环头插--反转部分链表92
九.力扣876-链表的中间节点 (要求遍历一次)
1.题目
2.图解
十.链表中第K个结点
tips:
代码
十一.分割链表
1.题目
2.图解编辑
3.代码
十二.回文链表
1.tips
2.代码
十三.160相交链表
十四.141.2环形链表
十五.快慢指针
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2:输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/remove-element
思路二:以空间换时间:不是val的数据放在新数组里,再把新数组的只拷贝过去(不符合题意);空间复杂度为O(n)
思路三用双指针:此时时间复杂度为O(N),空间复杂度为O(1)
1.src位置不是val就放到dst位置然后src++,dst++;
2.src位置是val,src++;
3.在src往后遍历过程中,在2的前提下如果src!=val,就让src所指的元素移到dst所指向的位置
3.代码
int removeElement(int* nums, int numsSize, int val) { int src =0; int dst=0; while(src
203. 移除链表元素
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
struct ListNode* removeElements(struct ListNode* head, int val) { struct ListNode*prev=NULL; struct ListNode*cur=head; while(cur) { //1.是头部节点 //2.删除中间元素 if(cur->val==val) { if(cur==head) { head=cur->next;//如果直接先free,会把这个位置的值和next变成随机值 free(cur); cur=head; } else { prev->next=cur->next; free(cur); cur=prev->next; } } else { prev=cur; cur=cur->next; } } return head; }
nums1和nums2都从后往前走,取大的从后往前放(演示过程如下视频)如果从前往后覆盖,很有可能将num1前面的元素弄没。 也可以从后往前覆盖而后排序,但是效率会变低。。 会出现下面情况
时间复杂度为O(m+n),空间复杂度为O(1)
15-03-16
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
int end1=m-1;
int end2=n-1;
int end=m+n-1;
while(end1>=0&& end2>=0)
{
if(nums1[end1]>nums2[end2])
{
nums1[end]=nums1[end1];
--end;
--end1;
}
else
{
nums1[end]=nums2[end2];
--end;
--end2;
}
}
//如果是nums1没完不需要处理,因为就是归于nums1里面
while(end2>=0)
{
nums1[end]=nums2[end2];
--end;
--end2;
}
}
如果left为偶数,right为奇数,则交换两数的位置(其中有3处易错点已用注释标出)
#include
void swap(int* arr, int sz)
{
int left = 0;
int right = sz - 1;
while (left < right)
{
while ((left < right) && (arr[left] % 2 == 1))//如果arr数组中全是奇数,虽然不会报错但是你会越界访问!!!
{
left++;
}
while ((left
一组数据中只有一个数字出现了一次,其他所有数字都是成对出现的,请找出这个数字采用位运算(同0异1)
^ 小知识复习:
0与任何一个数字异或的结果都等于这个数;两个相同的数字异或的结果为0;
eg: 1 2 3 1 2 五个数字进行异或,结果为 3;
#include
int main()
{
int arr[] = { 1,2,3,4,5,1,2,3,4 };
int ret = 0;
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
ret ^= arr[i];
}
return 0;
}
五.剑指offer---计算一个整数在计算机存储的二进制位中1的个数
*这道题需要注意负整数哦
代码1
(这样子写在当年的谷歌面试中并未得到欣赏,但是我可能还想不出来这个"......")
#include
int main() { int a = -1;//-1在计算机中的补码是32个1哦 int count = 0; for (int i = 0; i < 32; i++) { if ((1 & (a >> i)) == 1) count++; } printf("%d", count); return 0; } 代码2
分析:以15举例(&是同1异0)
num num-1 num&(num-1) 结果
赋值给下一个num
num中1的个数 1111 1110 1110 4 1110 1101 1100 3 1100 1010 1000 2 1000 0111 0000 1 #include
int main() { int a = -1; int count = 0; while (a) { a = a & (a - 1); count++; } printf("%d", count); return 0; } 3.或者就是写一个函数,参数中改为(unsigned int a)
1.给定一个x的值为0,让他与[0,n]中所有的数字异或,再和数组中的每个值进行异或,最后所得到的值就是消失的数字;(回到题目四)//注意:[0,n]是n+1个数字哦
2.(0+...n)-(arr[0]+...arr[n-1]) 时间复杂度O(n),空间复杂度O(1);
3.。。。
给你一个数组,将数组中的元素向右轮转 k
个位置,其中 k
是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3 输出:[5,6,7,1,2,3,4]
解释: 向右轮转 1 步:[7,1,2,3,4,5,6]
向右轮转 2 步:[6,7,1,2,3,4,5]
向右轮转 3 步:[5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100] 解释: 向右轮转 1 步: [99,-1,-100,3] 向右轮转 2 步: [3,99,-1,-100]
进阶:
O(1)
的 原地 算法解决这个问题吗?1.暴力求解---循环k次---O(n*k)(可以看看左旋数组-代码整合那个博客,思路是一样的)
2.额外开辟空间
3.左逆序右逆序全部逆序----可以做到以O(1)的空间复杂度 注意栈溢出的那点哦
void rotate(int* nums, int numsSize, int k) { if(k>numsSize)//没有这步的话会栈溢出 { k=k%numsSize; } Reverse(nums,0,numsSize-k-1); Reverse(nums,numsSize-k,numsSize-1); Reverse(nums,0,numsSize-1); } void Reverse(int*nums,int left,int right) { while(left
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2] 输出:[2,1]
示例 3:
输入:head = [] 输出:[] 提示:
[0, 5000]
-5000 <= Node.val <= 5000
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode*cur=head;
struct ListNode*newNode=NULL;
while(cur)
{
struct ListNode*next=cur->next;
//头插
cur->next=newNode;
newNode=cur;
//迭代
cur=next;
}
return newNode;
}
2-迭代
//n1--prev,n2--cur,n3--next便于迭代,不然n1,n2之间的链表断开,怎末走到下一个
//反转的终止条件是n2为空
struct ListNode* reverseList(struct ListNode* head)
{
if(head==NULL)//如果没有这步,下面代码中n2=head,n3=head->next就会出现空指针报错的情况
{
return NULL;
}
struct ListNode*n1,*n2,*n3;
n1=NULL;
n2=head;
n3=head->next;
while(n2)
{
//核心逻辑-反转
n2->next=n1;
//迭代
n1=n2;
n2=n3;
if(n3)//如果没有这步会出现空指针报错
n3=n3->next;
}
return n1;
}
1.结束条件:只有一个结点或者没有结点;
2.处理第一个节点与子链表翻转后的头结点
3.返回最终的节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* successor = NULL; // 后驱节点
// 反转以 head 为起点的 n 个节点
struct ListNode*reverseN(struct ListNode* head, int n) {
if (n == 1)
{
// 记录第 n + 1 个节点
successor = head->next;
return head;
}
// 以 head.next 为起点,需要反转前 n - 1 个节点
struct ListNode* last = reverseN(head->next, n - 1);
head->next->next = head;
head->next = successor;
return last;
}
给你单链表的头指针 head
和两个整数 left
和 right
,其中 left <= right
。请你反转从位置 left
到位置 right
的链表节点,返回 反转后的链表 。
示例 1:
输入:head = [1,2,3,4,5], left = 2, right = 4 输出:[1,4,3,2,5]
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* successor = NULL; // 后驱节点
// 反转以 head 为起点的 n 个节点
struct ListNode*reverseN(struct ListNode* head, int n) {
if (n == 1)
{
// 记录第 n + 1 个节点
successor = head->next;
return head;
}
// 以 head.next 为起点,需要反转前 n - 1 个节点
struct ListNode* last = reverseN(head->next, n - 1);
head->next->next = head;
head->next = successor;
return last;
}
//
struct ListNode* reverseBetween(struct ListNode* head, int m, int n)
{
if (m == 1)
{
return reverseN(head, n);
}
head->next = reverseBetween(head->next, m - 1, n - 1);
return head;
}/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* successor = NULL; // 后驱节点
// 反转以 head 为起点的 n 个节点
struct ListNode*reverseN(struct ListNode* head, int n) {
if (n == 1)
{
// 记录第 n + 1 个节点
successor = head->next;
return head;
}
// 以 head.next 为起点,需要反转前 n - 1 个节点
struct ListNode* last = reverseN(head->next, n - 1);
head->next->next = head;
head->next = successor;
return last;
}
//
struct ListNode* reverseBetween(struct ListNode* head, int m, int n)
{
if (m == 1)
{
return reverseN(head, n);
}
head->next = reverseBetween(head->next, m - 1, n - 1);//这步会最终回到m==1那里
return head;
}
来源:力扣
struct ListNode* reverseBetween(struct ListNode* head, int left, int right){
struct ListNode*dummy=(struct ListNode*)malloc(sizeof(struct ListNode));//如果left等于一我们还得找头节点,所以此时我们创建一个头部哨兵节点,避免这种情况
dummy->next=head;
struct ListNode*pre=dummy;
for(int i=0;inext;//先走到left节点之前的一个
}
struct ListNode*cur=pre->next;
for(int i=0;inext;
cur->next=next->next;
next->next=pre->next;
pre->next=next;//帮你试过了这三个顺序不能变,否则会超出时间限制
}
return dummy->next;//感觉好像还需要free;???
}
给定一个头结点为
head
的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
采用双指针-分两种情况
struct ListNode* middleNode(struct ListNode* head) { struct ListNode*fast,*slow; fast=slow=head; while(fast&&fast->next) { slow=slow->next; fast=fast->next->next; } return slow; }
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有
6
个节点,从头节点开始,它们的值依次是1、2、3、4、5、6
。这个链表的倒数第3
个节点是值为4
的节点。示例:
给定一个链表: 1->2->3->4->5, 和 k = 2. 返回链表 4->5.tips:
fast先走k步,而后fast和slow共同走
考虑如果k大于数组长度,会造成空指针类型的报错,所以
代码
struct ListNode* getKthFromEnd(struct ListNode* head, int k) { struct ListNode*fast,*slow; fast=slow=head; //while(--k)//进去k-1次 while(k--)//进去k次 { //k大于链表的长度 if(fast==NULL) { return NULL; } fast=fast->next; } while(fast) { fast =fast->next; slow=slow->next; } return slow; }
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你不需要 保留 每个分区中各节点的初始相对位置。
链接:https://leetcode.cn/problems/partition-list-lcci
//注意头尾边界
struct ListNode* partition(struct ListNode* head, int x)
{
struct ListNode*lesshead,*lesstail,*greaterhead,*greatertail;
//开辟一个头结点方便后续尾插
lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
lesstail->next=NULL;
greaterhead=greatertail=(struct ListNode*)malloc(sizeof(struct ListNode));
greatertail->next=NULL;
struct ListNode*cur=head;
while(cur)
{
if(cur->valnext=cur;
lesstail=cur;
}
else
{
greatertail->next=cur;
greatertail=cur;
}
cur=cur->next;
}
lesstail->next=greaterhead->next;
struct ListNode*newNode=lesshead->next;
greatertail->next=NULL;
free(lesshead);
free(greaterhead);
return newNode;
}
1.双指针遍历
2.先找链表中部节点,后半部分链表翻转,判断前面链表与后面翻转后的链表中的元素(奇数或偶数个元素都没关系,也没必要去取消连接);
//快慢指针找中值;翻转后半部分的链表;前部分后部分进行比较
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode*cur=head;
struct ListNode*newNode=NULL;
while(cur)
{
struct ListNode*next=cur->next;
//头插
cur->next=newNode;
newNode=cur;
//迭代
cur=next;
}
return newNode;
}
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode*fast,*slow;
fast=slow=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
bool isPalindrome(struct ListNode* head)
{
struct ListNode*mid=middleNode(head);
struct ListNode*rhead=reverseList(mid);
struct ListNode*curhead=head;
struct ListNode*currhead=rhead;
while(curhead&&currhead)
{
if(curhead->val!=currhead->val)
{
return false;
}
else//上一个相等了就继续去判断下个是否相等
{
curhead=curhead->next;
currhead=currhead->next;
}
}
return true;
}//快慢指针找中值;翻转后半部分的链表;前部分后部分进行比较
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode*cur=head;
struct ListNode*newNode=NULL;
while(cur)
{
struct ListNode*next=cur->next;
//头插
cur->next=newNode;
newNode=cur;
//迭代
cur=next;
}
return newNode;
}
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode*fast,*slow;
fast=slow=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
bool isPalindrome(struct ListNode* head)
{
struct ListNode*mid=middleNode(head);
struct ListNode*rhead=reverseList(mid);
struct ListNode*curhead=head;
struct ListNode*currhead=rhead;
while(curhead&&currhead)
{
if(curhead->val!=currhead->val)
{
return false;
}
else//上一个相等了就继续去判断下个是否相等
{
curhead=curhead->next;
currhead=currhead->next;
}
}
return true;
}
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists
时间复杂度O(n)
尾节点相同就是相交
找交点:长的链表先走(长度差步),然后俩同时走,第一个相同的就是所求。
fast一次走两步,slow一次走一步,不带环fast会为空,带环,则两者相遇。
bool hasCycle(struct ListNode *head) { struct ListNode*fast=head,*slow=head; while(fast&&fast->next)//如果链表中的元素是奇数个并且不是环状 { slow=slow->next; fast=fast->next->next; if(slow==fast) { return true; } } return false; }
1.双指针fast语slow,fast一次走两步,slow一次走一步,fast与slow一定在环内相遇,如果fast一次走的不是2步(n>2),则不一定。
2.环的入口如何找?
一个指针从相遇点开始走,另一个从链表头开始走,他们会在环的入口点相遇。
L=N*C-x,C指的是环的长度,x代表从入环点到相遇位置的距离,L表示从链表头到入环点之间的距离,N表示再相遇前,快指针在环内走的圈数。
L=(N-1)*C+C-x (N-1)*C表示他从meetnode又走到meetnode
struct ListNode *detectCycle(struct ListNode *head) { struct ListNode*fast=head,*slow=head; while(fast&&fast->next) { slow=slow->next; fast=fast->next->next; if(slow==fast) { //相遇 struct ListNode*meet=slow; //公式证明的 while(meet!=head) { meet=meet->next; head=head->next; } return meet; } } return NULL; }
给你一个链表,删除链表的倒数第
n
个结点,并且返回链表的头结点。示例 1:
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]给你一个链表,删除链表的倒数第
n
个结点,并且返回链表的头结点。示例 1:
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]struct ListNode* removeNthFromEnd(struct ListNode* head, int n) { struct ListNode*newNode=(struct ListNode*)malloc(sizeof(struct ListNode)); struct ListNode*fast=head; struct ListNode*slow=newNode; newNode->next=head; for(int i=0;i
next; } while(fast) { fast=fast->next; slow=slow->next; } struct ListNode*temp=slow->next; slow->next=temp->next; free(temp); slow=newNode->next; free(newNode); return slow; }struct ListNode* removeNthFromEnd(struct ListNode* head, int n) { struct ListNode*newNode=(struct ListNode*)malloc(sizeof(struct ListNode)); struct ListNode*fast=head; struct ListNode*slow=newNode; newNode->next=head; for(int i=0;i next; } while(fast) { fast=fast->next; slow=slow->next; } struct ListNode*temp=slow->next; slow->next=temp->next; free(temp); slow=newNode->next; free(newNode); return slow; }