二分查找+双指针+链表

二分查找是一种在有序序列中进行检索的算法。

洛谷 P2249 【深基13.例1 】查找

通过比较中间值与要查找的数字的大小关系,确定搜索范围,不断进行二分搜索,
直到找到目标数字或搜索范围为空。

#include <bits/stdc++.h>
using namespace std;
int n,a[100000000],s;
int gg(int h){
    int l,r,mid;
    l=1,r=n;
    while(r>l){
        mid=l+(r-l)/2;
        if(a[mid]>=h) r=mid;
        else l=mid+1; 
    }
    if(a[l]==h) return l;
    else return -1;
}
int main(){int h;
    cin>>n>>s;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=s;i++){
    
    cin>>h;
    cout<<gg(h)<<" ";
    }
    
    return 0;
}

这题也可以用 STL 自带的二分函数—— lower_bound

函数 返回第一个大于等于 h 的数的地址。因为是地址,在最后要 -a。

#include <bits/stdc++.h>
using namespace std;
int n,a[100000000],s;
int main(){
	int h,m;
	cin>>n>>s;
    for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=s;i++){
	cin>>h;
	m=lower_bound(a+1,a+n+1,h)-a;
	if(a[m]==h) cout<<m<<" ";
	else cout<<"-1 ";
	}
	
	return 0;
}

P1102 A-B数对

 
这用常规的二分方法去做,稍微有些麻烦,使用标准库函数解决实际算法问题,效率很高。

#include <bits/stdc++.h>
using namespace std;

int main(){
	int n,h,a[200000];
	long long ans=0;
	cin>>n>>h;
    for(int i=1;i<=n;i++){
	cin>>a[i];
	}
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++){
	ans+=upper_bound(a+1,a+n+1,a[i]+h)-lower_bound(a+1,a+n+1,a[i]+h);
	}
	cout<<ans;
	return 0;
}

洛谷 P1873砍树

通过给每个数加上一个高度h,然后计算每个数比h高的部分的和cet,找到一个h的值使得cet大于等于
m,即可得到答案。使用二分查找,不断逼近h的值,并根据cet的大小进行更新l和r指针。最终输出middle即为答案。

#include <bits/stdc++.h>
using namespace std;
long long n,m,a[1000005],cet,l,r,mid;
int main(){
	cin>>n>>m;
    for(int i=1;i<=n;i++){
	cin>>a[i];
	if(a[i]>r) r=a[i];
	}
	while(l<=r){
	cet=0;
	mid=l+(r-l)/2;
	for(int i=1;i<=n;i++){
	if(a[i]>mid) cet+=a[i]-mid;
	}
	if(m>cet) r=mid-1;
	else l=mid+1;
	}
	cout<<r;
	return 0;
}

 

[NOIP2001 提高组] 一元三次方程求解

通过二分查找 在-100~100之间找到三个根,并确定它们的取值范围。然后通过画线段和判断交点的方法,找到每个焦点的值,不断靠近精确值。最后输出结果。

#include <bits/stdc++.h>
using namespace std;
double a,b,d,c,mark=0;

double f(double x){
	return a*x*x*x+b*x*x+c*x+d;
}
void find(double l,double r){
	if(r-l<0.001) {
	printf("%.2f ",r);
	return;
	}
	double mid=(l+r)/2;
		if(f(mid)==0) {printf("%.2f ",mid);return;
		}
		if(f(mid)*f(r)<0) find(mid,r);
		else find(l,mid);
		
}
int main(){
		cin>>a>>b>>c>>d;
    for(double i=-100;i<=100,mark<3;i++){
	if(f(i)==0) {
	printf("%.2f ",i);
	mark++;
	continue;
	}
	if(f(i)*f(i+1)<0) {
		find(i,i+1);
		mark++;
	}
	}
	return 0;
}

P1678 烦恼的高考志愿

题意是给定一组有序数组,要找到一个位置使得插入该位置后,相邻节点的差值最小。通过二分查找的方式,找到一个位置,然后进行差值比较,最终得到最小差值。

#include <bits/stdc++.h>
using namespace std;
long long  n,m,a[1000005],s=0;
int find(int h){
	int l=1,r=n;
	while(true){
		int mid=l-(l-r)/2;
		if(a[mid]==h)  return 0;
		else if(a[mid]<h && a[mid+1]>=h) return min(a[mid+1]-h,h-a[mid]);
		else if(a[mid]<h) l=mid+1;
		else if(a[mid]>h) r=mid;
	}
}
int main(){
	long long b[1000005];
	cin>>n>>m;
    for(int i=1;i<=n;i++){
	cin>>a[i];
	}
	for(int i=1;i<=m;i++){
	cin>>b[i];
	}
	sort(a+1,a+n+1);
	for(int i=1;i<=m;i++){	
	if(a[n]<b[i]) s+=b[i]-a[n];
	else if(b[i]<a[1]) s+=a[1]-b[i];
	else s+=find(b[i]); 
	}
	cout<<s;
	return 0;
}

当然也可以用lower_bound代替find()函数

#include <bits/stdc++.h>
using namespace std;
long long  n,m,a[1000005],s=0,k,b[1000005];
int main(){
	cin>>n>>m;
    for(int i=1;i<=n;i++){
	cin>>a[i];
	}
	for(int i=1;i<=m;i++){
	cin>>b[i];
	}
	sort(a+1,a+n+1);
	for(int i=1;i<=m;i++){	
	if(a[n]<b[i]) s+=b[i]-a[n];
	else if(b[i]<a[1]) s+=a[1]-b[i];
	else {
		k=lower_bound(a+1,a+n+1,b[i])-a;
		s+=min(a[k]-b[i],b[i]-a[k-1]);
	}
	}
	cout<<s;
	return 0;
}

 双指针

双指针比较灵活,可以大大降低时间复杂度,可用在数组,单链表等数据结构中。

快慢指针:一快一慢,步长一大一小。例如,是否有环问题(看慢指针是否能追上快指针),单链表找中间节点问题(快指针到单链表结尾,慢指针到一半)。

对撞指针:一左一右向中间逼近。

滑动窗口:一般是右端向右扩充,达到停止条件后右端不动,左端向右端逼近,逼近达到停止条件后,左端不动,右端继续扩充。

快慢指针

力扣 27. 移除元素

快指针来寻找新数组的元素 。新数组就是不含有目标元素的数组

慢指针指向更新 新数组下标的位置

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (val != nums[fastIndex]) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

对撞指针

力扣 977.有序数组的平方

两个指针,i指向头,j指向尾,数组m和nums数组一样的大小,让k指向m数组终止位置

如果nums[i]*nums[i] > nums[j]*nums[j] , 则m[k--] = nums[i] * nums[i];

如果nums[i]*nums[i] > nums[j]*nums[j] , 则m[k--] = nums[i] * nums[i];

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
	vector <int> m(nums.size(),0);
	int k = nums.size()-1;
	for (int i = 0,j = nums.size()-1; i <= j;){
		if (nums[i]*nums[i] > nums[j]*nums[j]) {
			m[k--] = nums[i] * nums[i];
			i++;
		}
		else {
			m[k--] =nums[j] * nums[j];
			j--;
		}
	}
	return m;
    }
};

力扣 844 比较含退格的字符串

同时从后向前遍历S和T(i初始为S末尾,j初始为T末尾),记录#的数量,模拟消除的操作,如果#用完了,就开始比较Si]和T[j]。

class Solution {
public:
    bool backspaceCompare(string s, string t) {
	int r = s.size( ) - 1,r2 = t.size( ) - 1, mark = 0, mark2 = 0;
	while(1) {
		while (r >= 0) {
			if (s[r] == '#') {
				mark++;
			}
			else {if (mark > 0){
                mark--;
			}
			else break;
			}
			r--;
		} 
		while (r2 >= 0) {
			if (t[r2] == '#') {
				mark2++;
			}
			else {if (mark2 > 0){
                mark2--;
			}
			else break;
			}
			r2--;
		} 
		 
			if (r < 0 || r2 < 0) break;if (s[r] != t[r2]) return false;
			r--;r2--;
	}
	if (r == -1 && r2 == -1) return true;
	return false;
	}
};

滑动窗口

力扣 209.长度最小的子数组

窗口是满足其和 ≥ target的长度最小的连续子数组。如果当前窗口的值大于s了,窗口就要向前移动了。窗口的结束位置就是遍历数组的指针。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
	int minx = INT_MAX, l = 0, sum = 0;
	for (int i = 0, j = 0; i < nums.size(); i++){
		sum += nums[i];
		while (j <= i && sum >= target) {
			l = i - j + 1;
			minx = min (minx,l);
			sum -= nums[j];
            j++;
		}
	}
	return minx == INT_MAX ? 0 : minx;
    }
};

链表

力扣 203.移除链表元素

设置虚拟头节点,原链表的所有节点就可以按照统一的方式进行移除

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
	ListNode *dummy = new ListNode(0);
	dummy -> next = head;
	ListNode *cur = dummy;
	while (cur -> next != NULL) {
		if (cur -> next -> val == val) {
			ListNode *tmp = cur -> next;
			cur -> next = cur -> next -> next;
			delete tmp;
		}
		else {
			cur = cur -> next;
		}
	}
	head = dummy -> next;
	delete dummy;
	return head;
    }
};

力扣 707.设计链表

这道题目设计链表的五个接口:获取链表第index个节点的数值 在链表的最前面插入一个节点 在链表的最后面插入一个节点 在链表第index个节点前面插入一个节点 删除链表的第index个节点。覆盖了链表的常见操作

class MyLinkedList {
public:
	struct ListNode {
		int val;
		ListNode *next;
		ListNode (int val) : val(val),next(nullptr){}
	};
	
    MyLinkedList() {
		dummyhead = new ListNode(0);
		_size = 0;
    }
    
    int get(int index) {
    if (index < 0 || index > _size-1) return -1;
	ListNode *cur = dummyhead -> next;
	while (index--) {
		cur = cur -> next;
	}
	return cur -> val;
    }
    
    void addAtHead(int val) {
	ListNode *p = new ListNode(val);
	p -> next = dummyhead -> next;
	dummyhead -> next = p;
	_size++;
    }
    
    void addAtTail(int val) {
	ListNode *p = new ListNode(val);
	ListNode *cur = dummyhead;
	while (cur -> next != nullptr) {
		cur = cur -> next;
	}
	cur -> next = p;
	_size++;
    }
    
    void addAtIndex(int index, int val) {
    if (index > _size) return;
    if (index < 0) index = 0;
	ListNode *p = new ListNode(val);
	ListNode *cur = dummyhead;
	while (index--) {
		cur = cur -> next;
	}
	
	p -> next = cur -> next ;
	cur -> next = p;
	_size++;
	
    }
    
    void deleteAtIndex(int index) {
	ListNode *cur = dummyhead;
	 if (index < 0 || index > _size-1) return;
	while (index--) {
		cur = cur -> next;
	}
	ListNode *tmp = cur -> next;
	cur -> next = cur -> next -> next;
	delete tmp;
	tmp = nullptr;
	_size--;
    }
      void printLinkedList() {
    ListNode *cur = dummyhead;
	while (cur -> next != nullptr) {
		cout << cur -> next -> val<<" ";
		cur = cur -> next;
	} 
	cout << endl;
	}
    
    
    
    private :
    	int _size;
    	ListNode *dummyhead;
};

力扣 206.反转链表

定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。开始反转  cur->next 节点用tmp指针保存。移动pre和cur指针。cur 指针已经指向了null,循环结束,链表也反转完毕。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode *p = head;
ListNode *pre = NULL;
ListNode *tmp;
while (p) {
	tmp = p -> next;
	p -> next = pre;
	pre = p;
	p = tmp;
} 
return pre;
    }
};

  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值