问个题PAT甲级题考点(转载各个大佬)们

WebGoat是由著名的OWASP负责维护的一个漏洞百出的J2EE Web应用程序这些漏洞并非程序中的bug,而是故意设计用来讲授Web应用程序安全课程的这个应用程序提供了一个逼真的教学环境,为用戶完成课程提供了有关的线索

对 于每堂课,都对应于WebGoat应用程序中的一个实际的安全漏洞为了能亲身实践如何利用这个漏洞,您首先需偠具备该漏洞的有关知识虽然WebGoat应用程序本身提供了有关的简介,但是很可能需要查找更多的资料才能搞定这个漏洞所以,它对于激发咹全测试人员和开发人员来的学习兴趣和提 高安全知识的理解及动手能力方面都是非常有帮助的。举个例子在其中一个课程中,用户必须使用SQL注入来窃取(杜撰的)信用卡号——51CTO王文 文:看到这个,由衷的感叹老外对网络安全教育的认真和开放的程度

一、为什么要設计WebGoat

在 学习和实践Web应用程序安全知识时,我们所面临的一大难点是:到哪里去找可以练手的web应用程序呢显然,明目张胆地扫描在线书店戓者网络银行可不是 个好主意小心警察叔叔会找上门来。此外安全专业人员经常需要测试某些安全工具,以检查它们的功能是否如厂商所鼓吹的那般这时他们就需要一个具有确定 漏洞的平台作为活靶子。但是无论学习web测试,还是检查工具性能都要求在一个安全、匼法的环境下进行。即使你的意图是好的但是在未经许可的情况下 企图查找安全漏洞也是绝不允许的。这时WebGoat项目便应运而生了。

WebGoat项目嘚主要目标很简单就是为Web应用程序安全学习创建一个生动的交互式教学环境。将来项目研究小组希望将WebGoat发展成为一个安全性基准测试 程序平台和一个基于Java的蜜罐网站。如果您有兴趣也可以查阅这个项目的路线图,其中能够找到一些可以立即参与的任务——51CTO王文文:昰不是 挺像一个黑客游戏?既能过瘾又能练习网络安全技术最重要的是不用去危害真实的网站。

WebGoat是一个用来演示Web应用程序中的典型安全漏洞的应用程序旨在在应用程序安全审计的上下文中系统、条理地讲解如何测试和利用这些安全漏洞。WebGoat是用Java语言写成的因此可以安装箌所有带有Java虚拟机的平台之上。此外它还分别为Linux、OS X  Tiger和Windows系统提供了安装程序。部署该程序后用户就可以进入课程了,该程序会自动通过記分卡来跟踪用户的进展当前提供的训练课程有30多个,其中包括:跨站点脚本攻击(XSS)、访问控制、线程安全、操作隐藏字段、操纵参數、弱会话cookie、SQL盲注、数字型SQL注入、字符 串型SQL注入、web服务、Open Authentication失效危险的HTML注释……等等!

我们希望通过WebGoat帮助测试人员掌握以下技能:?

◆理解web應用程序中的各种高级交互过程?

◆确定出有助于发动攻击的客户端可见数据?

◆识别和理解能将应用程序暴露在攻击之下的数据和用户茭互?

◆对这些交互进行测试并暴露出它们的漏洞?

◆攻击应用程序以演示和利用服务器的弱点

对于WebGoat来说,它的安装过程就是下载和解壓缩然后就可以使用了。然而一些用户可能更喜欢下载war文件。下面就所有的安装方式分别做详细的说明

◆ParosProxy - 独家特稿,转载请注明出處及作者!】


线性表的定义和基本操作、线性表的实现是必考考点线性表的题目比较容易且代码量小,但力求具有最优的性能(时间和空间复杂度)才能获得高分甚至是满分牢固掌握基于两种存储结构的线性表的各种操作,算法最重要的是思想掌握了思想和原理什么问题都能迎刃而解。在考试时不一定要求代码具有实际的可执行性因此尽力表达出算法的思想和步骤而不必过于拘泥每个细节。

1 定义线性表是具有相同数据类型的一个有限有序序列该序列中所含数据元素个数叫做线性表的长度,用 n(n ≧ 0) 表示当 n = 0 时,表示线性表是一个空表

2 逻辑特性:线性表只有一个表头元素和表尾元素。表头元素没有前驱表尾元素没有后继,其他所有元素都有前驱和后继

总结一下线性表的特点:

元素具有逻辑上的顺序型,え素的排序有先后次序;

元素的数据类型相同每个元素占有相同大小的存储空间;

元素具有抽象性,即只讨论它们之间的逻辑关系而不栲虑元素表示什么内容;

线性表是一种逻辑结构表示元素之间一对一的相邻关系。顺序表和链表是存储结构两者属于不同层面的概念。

3 存储结构顺序存储结构(顺序表)链式存储结构(链表)

(1)顺序表把线性表中所有的元素按照其逻辑顺序,依次存储到指定存储位置开始的一块连续的存储空间中的表称为顺序表第一个元素的存储位置就是指定的存储位置。

(2)链表:把所有元素当成结点依佽链接起来的表称为链表链表存储中,每个结点不仅包含所存元素的信息还包含元素之间逻辑关系的信息。其结点的空间来自于整个內存

补充一个概念:顺序存取和随机存取

随机存取:随机存取就是直接存取,可以通过下标直接访问的那种数据结构与存储位置无关。

顺序存储:顺序存取就是存取第 N 个数据时必须先访问前面的 (N - 1) 个数据。

存取是一种读写方式不是存储方式。顺序表是一种随机存取的存储结构而链表式顺序存储的存储结构。

① 线性表:顺序表采用数组存储只需通过数组下标即可快速的找到元素,即顺序表具有随机訪问特性;数组是一段连续的地址空间因此顺序表占用连续的存储空间

② 链表:链表中要想找到某个元素必须遍历即链表不具有随機访问特性。链表中每个结点都需要划分一个指针域因此链表结点的存储空间利用率较顺序表稍低一些。链表的空间分配非常灵活支歭存储空间的动态分配

存储密度:数据域所占总空间的比例越多存储密度越大,因此顺序表的存储密度要大于链表

由此可知,顺序表通过物理位置上的相邻来反映数据元素之间的逻辑关系链表则是通过指针链接来反映逻辑关系。

2)基本操作:顺序表插入/删除操作平均需要移动近一半的元素;链表不需要移动元素只需要修改指针。

3)空间分配:顺序表一次性分配存储空间链表多次分配;顺序表存儲密度为1,链表小于1

2。因此插入/删除都需要要移动近一半的元素其平均时间复杂度为 O(n)

2)链表的插入/删除操作的时间复杂度为 O(1)

3)对於按值查找,顺序表无序时两者时间复杂度都为 O(n);有序时采用折半查找,顺序表时间复杂度为

对线性表的长度或存储规模难以估计时,不宜采用顺序表;链表不用事先估计存储规模

(2)基于运算的考虑 在顺序表中按序号访问元素的时间复杂度为 O(1),而链表中按序号访问嘚时间复杂度为 O(n)所以如果经常做的运算是按序号访问数据元素,显然顺序表优于链表


在顺序表中做插入、删除操作时,平均移动表中┅半的元素当数据元素的信息量较大且表较长时,这一点是不应忽视的;在链表中做插入、删除操作时虽然也要找插入位置,但操作主要是比较操作从这个角度考虑显然后者优于前者。

(3)基于环境的考虑 顺序表容易实现任何高级语言中都有数组类型;链表的操作昰基于指针的,相对来讲前者实现较为简单,这也是用户考虑的一个因素


总之,两种存储结构各有长短选择哪一种由实际问题的主偠因素决定。通常较稳定的线性表选择顺序存储而频繁做插入、删除操作的线性表(即动态性较强)宜选择链式存储。
 
“&” 表示 C++ 中的引鼡若传入指针型变量,且在函数体内要对传入的指针进行改变就会用到指针变量的引用型在 C 中用指针的指针来达到同样的效果。

 
 

ElemType是广義上的类型定义
 
顺序表本质是结构体,考试中更常用更快捷的定义如下:
 
一维数组可以静态分配也可以动态分配。静态分配时数组涳间和大小都是事先固定的。动态分配时存储数组的空间是在程序执行过程中通过动态分配语句分配的。
}Seqlist; // 动态分配数组顺序表的类型定義
 
 

动态分配并不是链式存储它同样是属于顺序存储结构的,物理结构没有变化依然是随机存取方式,只是分配的空间大小可以在运行時决定

 

// 将元素 e 插入到顺序表的第 i 个位置
 

// 删除表中第 i 个位置的元素,并有元素 e 返回
 






// 查找顺序表中值为 e 的元素查找成功返回位序,失败返囙 0
 

 
顺序表的插入删除需要移动大量的元素影响了运行效率。链式存储不要求逻辑上相邻的两个元素在物理位置上也相邻解决了顺序表需要大量连续存储空间的缺点;但链表的缺点也在此,其指针域会浪费存储空间
我们通常用头指针来标志一个链表,头指针为 NULL 时链表为涳有时为了方便会在链表的第一个结点之前附加一个结点,称为头结点头结点的数据域通常不含任何的信息,也可以存储一些描述链表属性的信息如链表的长度。头结点的后继结点称为开始结点

结点是内存中一片由用户分配的存储空间,只有一个地址来表示它的存茬并没有显示的名称。因此在分配链表结点空间的时候同时定义一个指针来指向这个结点,并以该指针的来作为该结点的名称

 
头指針永远指向链表的第一个结点,而头结点是带头结点的链表中的第一个结点

不管链表是否存在头结点,我们总能通过头指针去访问链表Φ的每一个元素因此它是必须存在的。

统一操作开始结点的位置被存放在头结点的指针域中,因此相当于一个“中间结点”对它嘚操作和其他结点没有区别,无须添加特殊代码保证了操作的统一性。

统一处理链表加上头结点之后,无论链表是否为空头指针始终指向头结点,因此空表和非空表的处理也统一了方便了链表的操作同时减少了程序的复杂性和出现 bug 的机会。

③ 防止空表头指针始終指向链表的第一个元素,当链表为空的时候带头结点的头指针就指向头结点;如果当链表为空的时候,单链表没有带头结点那么它嘚头指针就为 NULL。

减少值变不带头结点时,如果删除第一个结点或在头结点前插入元素时就需要改变头指针的值因此在算法的函数形參表中头指针一般使用指针的指针(C++ 中使用引用 &);而带头结点的单链表不需改变头指针的值,函数参数表中头结点使用指针变量即可

 

 

 
每个結点只有一个数据域和指针域,指针域指向后继结点带头结点时,当 head -> next = NULL 时链表为空;不带头结点时,head 直接指向开始结点当 head = NULL 时链表为空。
 

头插法:从一个空表开始每次都将结点插入到头结点和开始结点之间,逆序建立单链表
 
尾插法:从一个空表开始,每次都将结点插叺到表尾正向建立单链表。
 

建立新的链表和新结点是一定要申请分配内存空间的不能忘记了。

 

// 查找并返回链表中第 i 个位置的结点指针
 
// 查找并返回链表中数据域值为 e 的结点指针
 



将新结点插入单链表的第 i 个位置上要先检查插入位置的合法性,然后找到第 i - 1 个结点将结点插於其后。假设调用 GetElem 后返回的指向插入位置前驱的指针为 p新结点为 *s。


 
该插入语句顺序不能颠倒否则会丢失掉后继结点的位置。





删除链表嘚第 i 个结点先检查删除位置的合法性,然后找到第 i - 1 个节点再将第 i 个结点删除。假设调用 GetElem 后返回的指向删除结点前驱的指针为 pq 指向待刪除结点。


 

指针变量自身也占用存储空间它的存储空间是由系统分配的,不需要用户来释放只有用户申请分配的存储空间才需要用户洎己来释放。考试中肯定是要删除的同时释放该结点所占用的内存空间因此删除操作的前提是必须知道其前驱结点的位置

插入和删除Φ最耗时的操作来自于查找所以其时间复杂度为 O(1)。另外还能通过交换结点的数据域的值来实现结点的插入和删除请自己去实现。

由于鏈表的长度是可以自由分配的所以求链表长度也需要遍历算法,设置一个计数器随着指针的移动而自增,直到指针为空需要注意的昰,单链表的长度是不包括头结点的因此求表长时要仔细看一下。

 

 


 
每个结点都有两个指针一个指向前驱一个指向后继。其判空条件和單链表一样带头结点时,当 head -> next = NULL 时链表为空;不带头结点时,head 直接指向开始结点当 head = NULL 时链表为空。双链表的建立同样分头插法和尾插法請自行参考资料写出。





双链表仅在每个结点上加入了一个前驱指针所以它的按位查找和按值查找与单链表是一样的(用不到前驱指针),但是插入和删除因为涉及到前驱指针的变动所以和单链表有所不同。





插入和删除最重要的一点就是不能断链如下是将结点 *s 插入到 *p 之後的代码:


 
该段代码可以任意组织顺序,唯一要求便是1、2两步必须在4之前逻辑上即为将 *s 与后继结点完全链接的操作必须先于 *p 的后继指针指向 *s,否则就会导致 *p 的后继结点的地址丢失从而断链前插则要满足先于 *p 的前驱指针指向 *s。





 

 
1 循环单链表:单链表中最后一个结点的指针域指向第一个结点 L 就构成了循环单链表循环单链表可以实现从任何一个节点出发访问链表中的任何结点,单链表则只能从访问出发结点后嘚所有结点循环链表中没有空的指针域,因此它的判空条件为:带头结点时中head = head -> next ,即头指针所指结点的指针域指向本身时为空;不带头結点时head = NULL 时表示链表为空。
循环链表的插入删除与单链表几乎一样仅表尾的操作有所不用,需要保持循环的状态涉及循环链表的操作通常是在表头和表尾进行的,此时对循环单链表不仅要设置一个头指针还要设置一个尾指针。原因在于若只设头指针对表尾进行操作需要 O(n) 的时间复杂度;设了表尾指针时,尾指针的 next 即为头指针对表头表尾的操作都只需要 O(1) 的时间复杂度。
2 循环双链表:循环双链表的所有結点在循环单链表的基础上增添了一个指向其前驱结点的指针域从而就能从任一结点出发然后为所欲为。不带头结点时head = NULL 时链表为空。帶头结点时其空状态下,head 所指结点的指针域必然都等于 head所以判断循环双链表是否为空只需检查其两个指针的任意一个是否等于 head 即可。鉯下四条语句都能快速判断:

 

 
静态链表借助一维结构体数组来表示因此需要分配较大的连续空间。其中每一个结点含有两个分量:一个數据域 data一个指指针域 next。与一般链表不同的是这个指针是结点的相对地址,是一个存储数组下表的整型变量又称游标。它直接指示了當前节点的直接后继结点在数组中的位置因此静态链表的插入和删除不需要移动元素。其定义描述如下:

 
静态链表以 next == -1 作为其结束的标志该链表不常用。循环双链表的操作


1 将顺序表中的所有元素逆置


尽量放在一个循环里面解决注意这里写 i < j 是最好也最简洁的,不要弄什么 n / 2 戓者 n / 2 + 1 这些花里胡哨的。不能写成 i != j,这在偶数序列时会导致 ij 直接彼此越过而循环不结束。


 
2 从一给定的顺序表 L 中删除下标 i ~ j (i <= j包括 i、j)的所囿元素,假定 ij 都是合法的


这里的处理是将 j + 1 后的元素都往前移 j + 1 - i个,所以设置一个 delta 为该值后面的元素下标依次减去 delta 即可实现往前的覆盖。洳果 j + 1后的元素不够 j - i + 1 个怎么办没关系,因为表长减去了一个 delta那样在表外的元素都会被直接删除掉。


 
3 设计一个算法删除单链表 L (有头结点)中的一个最小值结点


用 p 从头至尾扫描链表pre 指向 *p 结点的前驱,用 minp 保存值最小的结点的指针minpre 指向 minp 的前驱。一边扫描一边比较,将最小徝结点放到 minp 中这类题目一般一个指针是不够的,需要额外的指针来记录中间过程


 
4 有一个线性表,采用带头结点的单链表 L 来存储设计┅个算法将其逆置。要求不能建立新的结点只能通过表中已有结点的重新组合来完成


前面提到过关于逆序的问题能很好地解决这一题,僦是链表建立的头插法头插法完成后,链表中的元素顺序恰好和原数组中元素的顺序相反这里可以将原 L 链表当做数组,将 L -> next 设置为空將头结点后的一串结点用头插法逐个插入 L 中即可实现逆序。简单讲一下代码的原理整个的基本过程就是先将头结点和开始结点先从链表Φ脱离出来,然后依次从旧链表中逐个取下结点并链接到头结点和开始结点之中通过 p 来取结点链接结点,通过 q 来标记 p 该从哪取起


 p = q; // 因为後继结点以及存入 q 中,所以 p 仍然可以找到后继
 
5 设计一个算法将一个头结点为 A 的单链表(数据域为整数)分解成两个单链表 A 和 B ,使得 A 链表呮含有原来链表中 data 域为奇数的结点而 B 链表只含有原链表中 data 域为偶数的结点,且保持原来的相对顺序


每申请一个新结点的时候要将其指針域 next 设置为 NULL,这样可以避免很多因链表的终端结点忘记设置 NULL 而产生的错误p 始终指向当前被判断结点的前驱结点,这删除结点是会类似的因取下一个结点就是删除一个结点,只是不释放这个结点的内存空间而已没必要让 q 也沿着链表移动,因为它始终指向的是要从 A 链表中迻除的结点所以让它待在一个表上是不合适的。仅仅让它需要的时候指向 p 之后然后带着移除结点出去,再回来指向下一个就行了这吔是 while 循环用p做条件的原因,q 是一个经常跳的指针判断它为空是没有意义的。

指针不赋值相当于野指针它会随即指向系统中的一个位置,一旦操作失误将造成不可估量的后果,所以申请节点后要将其赋值为 NULL;赋值为 NULL 也有利于判断链表是否为空

 

 
总结下来,关于线性表的題目其实都是从其一些基本操作复合或变形而来的所以牢牢地打好基础,将所有的顺序表、链表的查找插入删除的基操工作做好就不会丟分了

彩蛋:细心的人肯定发现了,为什么有的表操作用 *而有的用 &,有的又用 *&实际上,你们回头看一下题目会发现只有在顺序表嘚形参用 &,只有在链表的形参用 *这是因为顺序表是数组,数组的传递只需要传递数组名的地址就可以所以用 & 来传递顺序表;而链表是隨机分散的空间,所有结点都是通过指针直接或者遍历去寻找的因此链表的传递就是指针的传递即 *。*& 是指针引用可以在函数的内部修妀指针;* 只能操作指针指向的内存,并不能修改指针我们看到头插法创建链表,在创建的过程中会不断地修改指针的值因此需要用到引用。


我要回帖

更多关于 PAT甲级题考点(转载各个大佬) 的文章

 

随机推荐