一、双指针
1.定义
双指针并非是指针变量的指针, 实际上就是两个索引。可以根据实际情况不断移动。 经常用于链表、数组这样的线性结构中。
双指针的适用场景有以下三种:
普通的指针:多是两个指针往同一个方向移动;
对撞的指针(多用于有序的情况):两个指针面对面移动(比如一头一尾往中间移动);
快慢的指针:慢指针+快指针。
1.1对撞指针
1.1.1 逆序对
逆序对就是序号 i>j,但是 A[j]<A[i]
此处使用了对撞的两个指针 j 和 k ,k指针从后向前找到 j ,A[k]与A[j]进行比较
// 给定一个长度为n的排列,求其逆序对数
// 第1行输入一个数n表示排列长度
// 接下来一行n个用空格隔开的数,表示这个排列
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
//逆序对就是 后一个数比前一个数大
int a[200000];
int main() {
int n;
int i, j, k;
scanf("%d", &n);
for ( i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
int sum = 0;
//蓝桥杯评测系统出错,因为不能在for循环中初始化增量
//int j ,int k,写在for循环外部
for(j = 0;j<n;j++){
for (k = n - 1; k >= j; k--) {
if (a[k] < a[j]) {
sum += 1;
}
}
}
printf("%d", sum);
return 0;
}
1.1.2 字符串翻转
要求在原字符串上修改,因此直接首尾进行交换
void reverseString(char* s, int sSize) {
//传进来数组起始地址和数组大小
char* left = s;//指向起始地址
char* right = s + sSize - 1;//终止字符
//直接修改原字符串的话,实际就是通过中间变量交换两个首尾
while (left < right) {
char ch = *left;
*left = *right;
*right = ch;//交换了两个字符,需要继续向中间靠拢
left++;
right--;//没有加*就说明是地址变化
}
}
1.2 快慢指针
1.2.1 反转链表
注意空指针使用
1.首先将fast->next保存到 temp 中,避免下一步无法操作.
2.把 slow 赋值给 fast->next 注意赋值关系,注意这里的赋值关系,必须是fast->next = slow 这样才能真正转向,已经把fast->next保存起来了,需要操作的对象也是fast->next.
3.最后要fast和slow继续向下走,先把fast赋值给slow,再把temp赋值给fast
struct ListNode* reverseList(struct ListNode* head){
//快慢指针
struct ListNode* slow = NULL;
struct ListNode* fast = head;
//快指针比慢指针快,对链表的下一个节点断链再转向
while(fast){
struct ListNode* temp;//临时保存的节点
temp = fast->next;//给链表转向
fast->next = slow;//注意赋值关系
slow = fast;//slow指针向前走
fast = temp;//fast指针指向后面的节点
}
return slow;//最后fast指向空,slow是头部指针
}
1.2.2 链表交点
利用快慢指针。
此题提供了一个关于快慢指针的思路。双指针不仅需要考虑方向、快慢,也需要考虑两指针之间的步长。
链表相交原题链接
思路:
求出两个链表之间的长度差距后,使链表较长的的那个指针移到两个后续一样长的位置。
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) {
//找交叉点
struct ListNode* A = headA;//计算长度
struct ListNode* B = headB;
//利用快慢指针,根据两个链表的长度来寻找应该先让快指针走几步
int sum_A = 0, sum_B = 0;
//计算链表长度
while (A) {
A = A->next;
sum_A++;
}
while (B) {
B = B->next;
sum_B++;
}
int result = sum_A - sum_B;
struct ListNode* p_A = headA;//用来减少节点差距
struct ListNode* p_B = headB;
if (result > 0) {
for (int i = 0; i < result; i++) {
p_A = p_A->next;
}
}
else if (result < 0) {
result = sum_B - sum_A;
for (int j = 0; j < result; j++) {
p_B = p_B->next;
}
}
//printf("%d %d",sum_A,sum_B);
while (p_A) {
if (p_A == p_B) {
return p_A;
}
p_A = p_A->next;
p_B = p_B->next;
}
return NULL;
}
二、查找算法
1.二分查找
适用于递增顺序表
1.1查找整数
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int divide(int a[], int key,int start,int end) {
int i;
while (start <= end) {
i = (start + end) / 2;
if (key < a[i]) {
end = i - 1;//key在a[i]的左半部分
}
if (key == a[i]) {
return i;//因为找第一个出现的
//printf("%d", i);
}
if (key > a[i]) {
start = i + 1;//key在a[i]的右半部分
}
}
return -1;
}
int main() {
int n,i,key;
int a[10000];
scanf("%d", &n);
for (i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
scanf("%d", &key);
int start = 1, end = n;
int index = divide(a, key, 1, n);
printf("%d", index);
return 0;
}
2.顺序查找
顺序查找就是从起始节点开始,逐个查找节点,直到找到关键词为止。
while循环中包括if判断条件,数据量较大时效率低,因此引入一个虚假的变量,while循环中只有一个判断条件,改变i的值即可
//题目说明:给出一个包含n个整数的数列,问整数a在数列中的第一次出现是第几个。
//使用优化过的顺序查找
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
int n,i,key;
int a[10000];
scanf("%d", &n);
for (i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
scanf("%d", &key);
int j = 0;//输出的下标
a[n] = key;//设置一个虚拟变量a[n]
while (key != a[j]) {
j++;
}
if (j < n)printf("%d", j + 1);
else printf("-1");
return 0;
}