算法竞赛入门到进阶 读书笔记(5)

本文介绍了算法竞赛中两种常用的数据结构——线段树和树状数组。线段树能有效处理区间操作,适用于解决POJ 2182这类问题,通过构建二叉树和定制查找函数降低复杂度。树状数组则是一种利用二进制特性进行检索的结构,适用于元素修改和求和,其lowbit函数和模板化代码简化了问题解决。对于同一问题,线段树构建和查找函数较为复杂,而树状数组代码简洁,便于理解和应用。
摘要由CSDN通过智能技术生成

一、线段树
1.线段树是一种用于区间处理的数据结构,用二叉树来构造。树的每个结点代表线段[L,R]。当L==R时代表这个结点只有一个点,即为叶子结点;如果L<R则代表这个结点是一个区间,还可以继续往下分为左儿子和右儿子。
线段树的操作次数是(log 2 n),复杂度优于直接查找。
通过树结点的延伸可知,数量n的区间树最多有4n个结点,因此要设置4n的储存空间。
poj 2182: 有编号1-n的牛,对于每个位置的牛,知道前面编号比它小的有多少头,求编号的顺序。
给出小的个数的数组c[n] ,通过列举可以发现a[n]=c[n]+1。a[n]是剩下的编号中第几大的数,通过这个规律用暴力查找可以很容易的做出来

  for(i=n;i>=1;i--)
{ k=0;
for(j=1;j<=n;j++)
{ if(num[j]!=-1) k++;  //数组num指的是初始化1.2.3...n的编号
 if(k==c[n]+1) { a[i]=num[j]; num[j]=-1; break;} //依次查找,满足条件则记下其值为-1
 } }

但是上述代码用到了二重循环,有n平方的复杂度,容易超时,用线段树可以解决这个问题。暴力查找和线段树做法的区别就在于直接查找在每个位置的答案中都要至多执行1-n的循环,而线段树的查找用到了二叉树的查找方法,分线段优化了查找效率。
首先构建二叉树:

void buildtree(int left,int right,int u)
{  tree[u].l=left;
  tree[u].r=right;
  tree[u].len=right-left+1;
  if(left==right)  return; //叶结点,不用递归下去了
  buildtree(left,(left+right)>>1,u<<1); //递归左子树
  buildtree(((left+right)>>1)+1,right,(u<<1)+1); //递归右子树
  }

构建好树后就要构建运用二叉树来解题的函数了,根据树和题意可知:如果第c[n]+1大的数大于左子树线段长度,就要转到右子树继续进行查找;如果小于左子树线段长度,继续查找左子树的子树即可

int chazhao(int u,int k)
{ tree[u].len--;
if(tree[u].l==tree[u].r) return tree[u].l; //找到答案
if(tree[u<<1].len>=k) return chazhao(u<<1,k);  //不同情况的子树搜索
if(tree[u<<1].len<k) return chazhao((u<<1)+1,k-tree[u<<1].len);
}

完成建树和构建函数后,在合理编写主函数即可,上述使用的是普通二叉树,还可以使用完全二叉树,编程可能更为简便。

2.比赛时二叉树可能会超过存储空间,用离散化的思想可以解决。
离散化思想就是提取、排序并删除相同的端点,将原二叉树压缩,但并没有改变覆盖关系。

二、树状数组
树状数组是一种利用二进制特征进行检索的树状结构。树状数组十分高效并且代码比较简洁。用于修改元素和求和,复杂度缩减到了log2 n。
树状数组核心的是lowbit(x),lowbit(x)=x&-x,功能是找到x的二进制数的最后一个 1。原理是利用负数的补码表示。(书上这里讲的很详细,但我还是没有完全理解)
树状数组有它的模板,还是先套用好了

#define lowbit(x) ((x)&-x)
void add(int x,int d)  //更新、修改数组
{ while(x<=n) {
tree[x]+=d;
x+=lowbit(x);}
}
int sum(int x)   //求和
{ int sum=0;
while(x>0){
  sum+=tree[x];
  x-=lowbit(x);}
return sum;}

poj 2182也可以用树状数组实现,它将每一个编号位置的牛都看作是1,即a1=a2=a3…=1,它的和其实就是剩下的编号中第k大的数,因此可以直接用树状数组初始化,不用add函数进行初始化。每一次查找完答案后同样需要更新tree数组代表用过这个值.

  for(int i=1;i<=n;i++)
{ tree[i]=lowbit(i);}
for(int i=n;i>=0;i--)
{ int x=findpos(pre[i]+1);
   add(x,-1);
   ans[i]=x;}

前面套用模板即可,用add更新树状数组的值,用sum计算比它大的有几个数。来构建查找答案的函数,查找答案运用二分查找法的思想,逐渐缩小区间最后得到答案。

int findpos(int x){
int l=1;r=n;
while(l<r){
 int mid=(l+r)>>1;
 if(sum[mid]<x)
  l=mid+1;
  else r=mid;
  } return l;
  }

同一道题可以运用线段树和树状数组两种方法,可以看出线段树主要难度在于构建二叉树和根据题目编写查找函数,而树状数组直接套用了模板后只编写了一个简便的二分查找函数。树状数组的优点在于代码简洁,便于思考;而线段树的优点在于应用广泛,和题目的重合点高。

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、下4载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、下4载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值