大家好,我是教授.F
目录
对于C语言指针可谓是重中之重,写好了不仅对学习其他语言有好处,对面试也是至关重要。
两个指针,方向相反
目的是在一个顺序数组中,怎么找到数组中俩个元素的和等于目标元素。
例如数组:int data[] = { 1, 2, 7, 8, 15, 20 },目标值:int target = 9; 我们可以用两个指针,一个在前面,一个在最后面,往中间靠拢。
#include<stdio.h>
#include<stdlib.h>
//注意这里应该是int *类型,因为返回的是数组
int * twoNum(int data[], int size, int target)
{
int i = 0;
int j = size - 1;
int sum=0;
while (sum < target)
{
i++;
sum = data[i] + data[j];
}
while (sum > target)
{
j--;
sum = data[i] + data[j];
}
if (i >= j)
{
return NULL;
}
int* ret = malloc(sizeof(int) * 2);
ret[0] = i+1;
ret[1] = j+1;
return ret;
}
int main()
{
int data[] = { 1, 2, 7, 8, 15, 20 };
int target = 9;
int size = sizeof(data) / sizeof(data[0]);
int* ret = twoNum(data, sizeof(data) / sizeof(data[0]), target);
if (ret)
{
printf("%d %d", ret[0], ret[1]);
}
else
{
printf("找不到\n");
}
return 0;
}
两个指针,方向相同
实现有序数组的合并,合并后的新数组,保存有序
我们将两个数组进行合并,怎么保证相加后的数组的元素是按顺序排的呢?
我们可以通过一个指针指向一个数组,另一个指针指向另一个数组。将每一个数组中的元素一一比较,小的就传到新的数组中。
#include<stdio.h>
#include<stdlib.h>
int* merge(int data1[], int n, int data2[], int m)
{
int i = 0, j = 0;
int k = 0;
int* ret = malloc(sizeof(int) * (n + m));
while (i<n && j<m)
{
if (data1[i] < data2[j])
{
ret[k++] = data1[i++];
}
else
{
ret[k++] = data2[j++];
}
}
while (i<n)
{
ret[k++] = data1[i++];
}
while (j<m)
{
ret[k++] = data2[j++];
}
return ret;
}
int main()
{
int data1[] = { 2,5,7,8,10 };
int data2[] = { 1,3,4,6,9 };
int n = sizeof(data1) / sizeof(data1[0]);
int m = sizeof(data2) / sizeof(data2[0]);
int* ret = merge(data1, n, data2, m);
for (int i = 0; i < n + m; i++)
{
printf("%3d", ret[i]);
}
return 0;
}
两个指针,一快一慢(快慢指针)
判断一个单链表,是否存在“环” 我们可以用两个指针,指向同一个地方,但是一个走的比较快,一个走的比较慢。例如下面的代码,一个指针一次走一格,一个指针一次走两个格。然后在几次循环后,两个指针相遇,那能判断这个单链表存在“环”。
在下面代码中,我是判断存在“环”,并且输出环的起始位置。怎么找到环的起始位置是是一个难点。我们不能直接输出相遇的点,因为相遇的点不一定就是起始位置。所以我们还得思考怎么得出起始位置。
我是这么做的:当相遇时,用一个指针指向相遇位置,一个指针指向单链表的表头。这两个指针放在while循环中,直到两个指针相遇,就找到了环的起始位置。
为什么这样就找到起始位置了???
大家请看我代码中的解析。
#include <stdio.h>
#include <stdlib.h>
struct ListNode
{
int val;
struct ListNode* next;
};
typedef struct ListNode ListNode;
ListNode* detectCycle(ListNode* head)
{
ListNode* fast = head, * slow = head;
while (fast != NULL && fast->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
if (slow == fast)
{
ListNode* index1 = head, * index2 = slow;
/*在这段代码中,index1和index2分别是快慢指针相遇后的两个指针。
它们相遇的位置正好是环的起始位置。这个结论可以通过数学推导来理解。
假设从链表头到环的起始位置的距离是a,环的起始位置到他们相遇的位置的距离是b,
他们相遇的位置再回到环的起始位置的距离是c。这里b是快指针比慢指针多走的距离,
而c是环的剩余部分。当快慢指针相遇时,慢指针走的距离:a + b,
快指针走的距离:a + b + n * (b + c),这里n是快指针在环内循环的次数,
由于快指针的速度是慢指针的两倍,所以可以得到 2 * (a + b) = a + b + n * (b + c)。
根据以上等式可以解出a = (n - 1) * (b + c) + c,这表示从相遇位置再走a步会回到环的起始位置。
因此,当两个指针相遇后,让其中一个指针回到链表头,然后两个指针以相同的速度移动,
它们相遇的位置就是环的起始位置。当n等于2时,a=c*/
while (index1 != index2)
{
index1 = index1->next;
index2 = index2->next;
}
return index1;
}
}
return NULL;
}
int main()
{
// 创建一个有循环的链表用于测试
ListNode* head = (ListNode*)malloc(sizeof(ListNode));
head->val = 1;
head->next = (ListNode*)malloc(sizeof(ListNode));
head->next->val = 2;
head->next->next = (ListNode*)malloc(sizeof(ListNode));
head->next->next->val = 3;
head->next->next->next = head; // 使链表形成循环,连接到头节点
ListNode* cycleStart = detectCycle(head);
if (cycleStart != NULL)
{
printf("Cycle starts at node with value: %d\n", cycleStart->val);
}
else
{
printf("No cycle found in the linked list.\n");
}
// 释放链表节点的内存
free(head->next->next);
free(head->next);
free(head);
return 0;
}
两个指针,方向相同,但是起点不同
返回单链表的倒数第n个节点
例如一个单链表长度未知,我们想返回倒数第三个,那么我们就可以有一个指针指向表头,另一个指针指向第三个。不断往下走,直到一个指针为空,后面的指针就是指向倒数第三个元素了。
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct LNode
{
int val;
struct LNode* next;
}LNode,*LinkList;
int Search_k(LinkList list, int k)
{
LNode* p = list->next,*q = list->next;
int count = 0;
while (p != NULL)
{
if (count < k)//让p先走到第k个位置,然后剩下的让q走,
//q就能走到(链表长度-k)的位置,也就是倒数第k的位置
{
count++;
}
else
{
q = q->next;
}
p = p->next;
}
if (count < k)//节点数小于k
{
return 0;
}
printf("%d", q->val);
return 1;
}
int main()
{
//创建节点
LinkList list = (LinkList)malloc(sizeof(LNode));
list->next = NULL;
LinkList current = list;
for (int i = 1; i <= 5; i++)
{
LNode* newNode = (LNode*)malloc(sizeof(LNode));
newNode->val= i * 10;
newNode->next = NULL;
current->next = newNode;
current = newNode;
}
int k = 3;
int result = Search_k(list,k);
if (result)
{
printf("是倒数第%d个元素\n", k);
}
else
{
printf("这个链表比%d还短",k);
}
current = list->next;
while (current != NULL)
{
LinkList tmp = current;
current = current->next;
free(tmp);
}
free(list);
return 0;
}