前言
这篇博客主要讲解的内容是部分单链表OJ题
一、轮转数组
把对应图给画出来,这道题就很so easy了
void rotate(int* nums, int numsSize, int k) {
//法一:一步一步地移动,每次都把最后一个数据拿到最前面去,要移动数组
//但时间复杂度高
int n = numsSize;
while (k--)//移动k次
{
int tmp = nums[numsSize - 1];
//数组整体向后移一步,倒着移
for (int i = n - 1; i >=1 ; i--)//记录好初始与末尾,循环就好写了 arr[n-1]=arr[n-2] arr[1]=arr[0]
{
nums[i] = nums[i - 1];
}
nums[0] = tmp;
}
}
void rotate(int* nums, int numsSize, int k) {
//法二:创建一个新数组,依次先拷贝后面k个,在拷贝前面的就可以了,最后再将新数组拷贝给原数组
int copy[numsSize];
int n=numsSize;
//首先k必须为有效值
k%=numsSize;
for(int i=0;i<k;i++)
{
copy[i]=nums[n-k+i];//将原数组的后面k个拷贝到新数组的前面k个
}
for(int i=k;i<n;i++)
{
//再将原数组前面n-k个拷贝到新数组后面n-k个
copy[i]=nums[i-k];
}
//最后新数组拷贝给原数组
for(int i=0;i<n;i++)
{
nums[i]=copy[i];
}
}
void Reverse(int* arr, int left, int right)
{
while (left < right)
{
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
}
void rotate(int* nums, int numsSize, int k) {
//法三:旋转三次,先旋转后面k个
//在旋转前面,最后旋转整体就可以了
//万一k值太大了呢,所以要处理一下
int n = numsSize;
k %= n;
Reverse(nums, n - k, n - 1);
Reverse(nums,0, n - k - 1);
Reverse(nums,0, n - 1);
}
二、消失的数字
int missingNumber(int* nums, int numsSize) {
//法一:将1~n(1,2,3)的值加起来再减去数组的值(0,1,3)就是消失的数字
//为了防止越界,所以要边加边减
int ret = 0;
for (int i = 0; i < numsSize; i++)
{
ret = ret - nums[i];
ret = ret + i + 1;
}
return ret;
}
int missingNumber(int* nums, int numsSize) {
//法二:创造一组数字(0~n)然后全部与数组的值异或,用找单身狗的方法来找消失的数字
int ret = 0;
for (int i = 0; i < numsSize; i++)
{
ret ^= nums[i];
}
for (int i = 0; i <= numsSize; i++)
{
ret ^= i;
}
return ret;
}
三、返回倒数第k个节点
一图胜前言
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
int kthToLast(struct ListNode* head, int k) {
ListNode* slow = head;
ListNode* fast = head;
//先让fast走k步//k是有效的
while (k--)
{
fast = fast->next;
}
//这次两个同时一步一步的走,fast为空的时候slow就到了
while (fast)
{
fast = fast->next;
slow = slow->next;
}
return slow->val;
}
四、链表的回文结构
偶数个的图
奇数个的图
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
ListNode* reverse(ListNode* head)
{
ListNode* cur = head;
if (head == NULL)
{
return NULL;
}
ListNode* next =cur->next ;
cur->next = NULL;
while (next)
{
ListNode* next_next = next->next;
next->next = cur;
cur = next;
next = next_next;
}
return cur;//返回头节点
}
ListNode* find_mid(ListNode* head)
{
ListNode* slow = head;
ListNode* fast = head;
if (head == NULL)
{
return NULL;
}
while (fast&&fast->next)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
bool chkPalindrome(ListNode* head) {
// write code here
//先找到中间节点
ListNode* mid = find_mid(head);
//再逆转mid后面链表
ListNode* head2 = reverse(mid);
//现在开始挨着挨着比较
//但mid的前一个还连着mid要注意一下
//但是也不影响
ListNode* cur1 = head;
ListNode* cur2 = head2;
while (cur2)
{
if (cur1->val != cur2->val)
{
return false;
}
cur2 = cur2->next;
cur1 = cur1->next;
}
return true;
}
};
五、相交链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) {
//先计算两个链表的长度,那么长的链表就先走它们的差距步,等到链表相等时就是相交点
ListNode* curA = headA;
ListNode* curB = headB;
int countA = 0;
int countB = 0;
//先找到两个链表的长度
while (curA)
{
countA++;
curA = curA->next;
}
while (curB)
{
countB++;
curB = curB->next;
}
int dif = abs(countA - countB);
//找出长链表
ListNode* LongListNode = headA;
ListNode* ShortListNode = headB;
if (countB > countA)
{
LongListNode = headB;
ShortListNode = headA;
}
//长链表先走dif步
while (dif--)
{
LongListNode = LongListNode->next;
}
//两个一起走
while (LongListNode)
{
if (LongListNode == ShortListNode)
{
return LongListNode;
}
LongListNode = LongListNode->next;
ShortListNode = ShortListNode->next;
}
return NULL;
}
六、随机链表的复制
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*/
typedef struct Node ListNode;
struct Node* copyRandomList(struct Node* head) {
//先在原链表每个节点后插入一模一样的节点
ListNode* cur = head;
if (cur == NULL)
{
return NULL;
}
while (cur)
{
ListNode* next = cur->next;
ListNode* copy = (ListNode*)malloc(sizeof(ListNode));
copy->val = cur->val;
copy->next = next;
cur->next = copy;
cur = next;
}
//在处理copy的random
//copy的random==它前一个节点的random的下一个
cur = head;
while (cur)
{
ListNode* copy = cur->next;
ListNode* next = copy->next;
if (cur->random == NULL)
{
copy->random = NULL;
}
else
{
copy->random = cur->random->next;//精华
}
cur = next;
}
//链接新链表,还原新链表
cur = head;
ListNode* ret = head->next;
while (cur)
{
ListNode* copy = cur->next;
ListNode* next = copy->next;
if(next!=NULL)
copy->next = next->next;
cur->next = next;
cur = next;
}
return ret;
}
七、环形链表1
其中L为直线长
***此图说明不管是fast二倍速还是三倍速,最终一定会相遇的,多次重复实验可说明,当fast为4倍速,五倍速,等等倍速时,只要走的圈数够多,最终一定会相遇的,但是具体的证明我不太会,到底是个怎么回事我也说不明白,但这个结论就是正确的,不管如何举例都是正确的,有人知道为什么吗? ***
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode* head) {
//一个快指针走两步,一个慢指针走一步,因为相对速度为一,所以两个都进环时,快指针一定能追上慢指针,追上了说明带环
//快慢指针相等说明带环,若不带环,则一定走到了NULL
ListNode* fast = head;
ListNode* slow = head;
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
return true;
}
}
return false;
}
七、环形链表2
这里我们主要以fast为二倍速进行说明,因为这个相遇时所用的圈数是最少的,fast的速度越快,他们相遇所用的圈数就要更多,而且上面说了,因为我也不知道为什么会相遇,再加上二倍速最好说明,而且只有二倍速才满足这个小结论,所以就二倍速了。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* detectCycle(struct ListNode* head) {
//一个快指针走两步,一个慢指针走一步,因为相对速度为一,所以两个都进环时,快指针一定能追上慢指针,追上了说明带环
//快慢指针相等说明带环,若不带环,则一定走到了NULL
ListNode* fast = head;
ListNode* slow = head;
//先找到相交结点
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
break;
}
}
//判断是否是没有循环导致NULL而跳出循环
if (fast == NULL || fast->next == NULL)
{
return NULL;
}
//来个指针记录相交节点按照1的速度前行,再来个指针从head按照速度为1前行
ListNode* meet = fast;
ListNode* cur = head;
while (1)
{
if (meet == cur)
{
return meet;
}
meet = meet->next;
cur = cur->next;
}
}
总结
以上就是今天讲的单链表OJ题了,感觉还是挺不错的