【算法】双指针、查找

一、双指针

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值