读书笔记-数据结构C语言版

来源:互联网 发布:地下水埋深数据 编辑:程序博客网 时间:2024/05/18 13:07

数据元素之间的逻辑结构:集合,线性结构,树形结构,图形结构或网状结构

物理结构:顺序存储结构和链式存储结构

 

任何算法的设计取决于选定的数据(逻辑)结构,而算法的实现依赖于采用的存储结构。

 

线性表n个数据元素的有限序列。称i为数据元素ai在线性表中的位序。线性表示一个相当灵活的数据结构,它的长度可根据需要增长或缩短,可访问/插入/删除。

线性表的顺序表示指的是一组地址连续的存储单元依次存储线性表的数据元素。由于每一个数据元素的存储位置都和线性表的起始位置相差了一个和数据元素在线性表中的位序成正比的常数,因此只要确定了存储线性表的起始位置,线性表中任一数据元素都可以随机存取,所以线性表的顺序存储结构是一种随机存储的存储结构。在顺序存储结构的线性表中插入或删除一个数据元素,平均约移动表中一半的元素,时间复杂度为On)。

为了方便插入和删除操作,引入线性表的链式存储结构,它不要求逻辑上相邻的元素也相邻,即这组存储单元可以是连续的也可以是不连续的,因此它没有顺序存储结构所具有的弱点,但同时也失去了顺序表可随机存取的优点。为了表示每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,,对数据元素ai来说,除了存储其本身的信息之外(数据域),还需存储一个指示其直接后继的信息(即直接后继的存储位置)(指针域),这两部分信息合称为结点,n个结点链结成一个链表,最后一个结点的指针域为NULL.在单链表中,取得第i个数据元素必须从头指针出发寻找,因此,单链表是非随机存取的存储结构。

静态单链表:(类似一维数组的结构)假如有如上的静态链表S中存储这线性表(abcdefghi),Maxsize=11,要在第四个元素后插入元素e,方法是:先在当前表尾加入一个元素e,即:S[9].data = e;然后修改第四个元素的游标域,将e插入到链表中,即:S[9].cursor = S[4].cursor; S[4].cursor = 9;,接着,若要删除第8个元素h,则先顺着游标链通过计数找到第7个元素存储位置6,删除的具体做法是令S[6].cursor = S[7].cursor

双向链表:有两个指针域,其一指向直接后继,另一指向直接前趋。

栈和队列也是线性表,从数据结构角度看,其特殊性在于栈和队列的基本操作是线性表操作的子集,它们是操作受限的线性表。

栈的修改是按照后进先出的原则进行的。致使栈成为程序设计中的有用工具:

1、数制转换:假如N为输入的数,n为要转换的进制,例如将十进制231转换为8进制,过程如下:

N        N/n      N%n

231      28        7

28       3         4

3        0         3

则输出为347,可以看出,首先得到的应该是7,然后才是4,最后是3,但是要逆序显示,自然就类似压栈出栈的数据结构了(数组也可以实现,但是没有体现其本质)。所以,只需要初始化栈后,将N%n不断的压入栈底,需要注意的是如果要转换为16进制,则需要对大于9的数字作字符处理。

Push(S,N%8);  N=N/8;

2、括号匹配的检验

功能描述和实现算法:为了识别圆括号和方括号使用格式的正确性,在算法中设置一个栈,每读入一个括号,若是右括号,则或者使置于栈顶的急迫切的期待得以消解,或者是不合法的情况;若是左括号,则作为一个新的更急迫切的期待压入栈中,自然使原有的在栈中的所有未消解的期待的急迫性都降了一级。

3、行编辑程序

功能描述和算法实现:接受用户从终端输入的程序或数据,并存入用户的数据区。为了方便用户自己发现输入数据的错误及时删除更改,可设这个输入缓存区为一个栈结构,每当从终端接受了一个字符之后,先做如下判别:如果它既不是退格符也不是退行符,则将该字符压入栈顶,如果是一个退格符,则从栈顶删去一个字符,如果它是一个退行符,则将字符栈清空。

4、迷宫求解

功能描述:求迷宫从入口到出口的所有路径;

算法实现:这里使用的是“回溯法”,从入口出发,按某一方向向前探索,若能走通(未走过的),即某处可以到达,则到达一个新点,否则试探下一个方向;若所有的方向均没有通路,则沿原路返回前一点,换下一个方向继续试探,直到所有可能的通路都搜索到,或找到一条通路,或无路可走又返回到入口点。这里可以用一个栈来实现,每走一步,将该位置压入栈中,若该点无路可走,则出栈返回上一位置。需要解决的问题有四个:(1)迷宫的数据结构(eg:二维数组表示迷宫,0 /1 表示通路与墙)保证每个点都有八个方向可以试探;(2)迷宫中间每个点都有8个方向可以试探。其增量数组可以用一个1*2的二维数组move表述:x,y表示坐标位置。

         x  y

  0   0  1

  1    1     1

  2    1     0

  3    1     -1

  4    0     -1

  5    -1    -1

6    -1    0

7    -1    1

(3)栈中存放的元素设计:不仅要存放当前位置的坐标,还需要存放该点沿哪个方向走下去的

(4)为防止重复到达某点,对走过的点进行标记

队列是一种先进先出的线性表,它只允许在表的一端进行插入(队尾),另一端删除元素(队头)。

树形结构是一类重要的非线性数据结构,以数和二叉树最为常用,直观来看,数是以分支关系定义的层次结构。在计算机领域中,树可以用来在表示源程序的语法结构,在数据库系统中,树形结构也是信息的重要组织形式之一。

树是n个结点的有限集,在任意一个非空树中:(1)有且仅有一个特定的称为根结点(2)当n>1时,其余结点可分为m个不相关的子树。结点拥有的子树称为结点的度,度为0的结点称为叶子节点,数中结点的最大层次称为树的深度。

森林是m颗互不相交的树的集合。对树种每个结点而言,其子树的集合就是森林。

二叉树(重点)是另一种树型结构,它的特点是每个结点至多只有两颗子树(即二叉树中不存在度大于2的结点),并且二叉树的子树有左右之分,不可任意颠倒。深度为k的二叉树至多有2^k-1个结点。

遍历二叉树递归算法有三种情况:先根序遍历、中根序遍历、后根序遍历,限定先左后右的顺序。以先根序遍历为例:若二叉树为空,则空操作,否则:(1)访问根节点(2)先序遍历左子树(3)先序遍历右子树。对含n个结点的二叉树,其时间复杂度均为O(n)

深度优先搜索:类似于树的先根遍历,假设初始状态是图中所有的顶点未曾被访问到,则从中某个顶点v出发,访问此顶点,然后依次从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相同的顶点都被访问到;若此时图中还有顶点未被访问到,则选择图中一个未曾被访问的顶点作为起始点,重复上述过程,直至图中所有顶点都被访问到为止。

广度优先搜索:类似于树的按层次遍历。假设图中某顶点v出发,在访问了v之后,依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直至图中所有已被访问的顶点的邻接点都被访问到。若此时图中尚有顶点未被访问,则选择图中一个未曾被访问的顶点作为起始点,重复上述过程,直至图中所有顶点都被访问到为止。

有序表的查找:折半查找,这个查找过程可以用二叉树描述,树中每个结点表示表中一个记录,结点中的值为该记录在表中的位置。某次查找在二叉树中可以表示为一条从根到某个叶子结点的路径。折半查找法在查找成功时和给定值之间进行比较的关键字个数至多为log2n+1个,也就是这个判定树的深度。

排序:有序的顺序表采用折半查找法,其平均查找长度为log2(n+1)-1,而无序的顺序表智能进行顺序查找,其平均查找长度为(n+1/2,所有遍历查找之前的排序很重要。按照排序过程中涉及的存储器不同,将排序方法分为两大类:内部排序(待排序记录存放在计算机随机存储器中进行排序过程)和外部排序(待排序记录的数量很大,以致内存依次不能容纳全部记录,在排序过程中尚需对外村进行访问的排序过程)。

内部排序待排序记录有以下三种存储方式:(1)存放在数据地址连续的一组存储单元上,比如数组,实现排序必须借助移动记录(2)存放在静态链表中,实现排序仅需修改指针(3)数据本身存储在地址连续的一组存储单元上,另设一个指示各记录存储位置的地址向量,实现排序需要移动这些记录的“地址”。第一种比较常见。

插入排序,将记录插入一个已排号序的一组记录。直接插入排序的时间复杂度为On^2;

折半插入排序,当待排序序列记录数量大时可采用,仅减少了关键字间的比较次数,时间复杂度为On^2;

希尔排序:先将整个待排记录序列分割成若干个子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时(进行最后一趟增量为1的直接插入排序),再对全体记录进行一次直接插入排序。子序列的构成不是简单的“逐段分割”,而是将相隔某个“增量”的记录组成一个子序列。

起泡排序12比,23比……,“比小沉小,比大沉大”,然后再从1比到n-1。总的时间复杂度为O(n)

快速排序:通过一趟排序将待排记录分割成两个部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,已达到整个序列有序。实现过程:任选一个记录作为枢轴,然后将小于这个数的记录都安置在这个数的左边,大于这个记录的安置在右边,这个过程叫一趟快速排序。时间复杂度为O(nlogn) 。实现代码如下:

 static void quicksort(int n[], int left, int right) {

        int dp;

        if (left < right) {

            dp = partition(n, left, right);

            quicksort(n, left, dp - 1);

            quicksort(n, dp + 1, right);

        }

    }

    static int partition(int n[], int left, int right) {

        int pivot = n[left];

        while (left < right) {

            while (left < right && n[right] >= pivot)

                right--;

            if (left < right)

                n[left++] = n[right];

            while (left < right && n[left] <= pivot)

                left++;

            if (left < right)

                n[right--] = n[left];

        }

        n[left] = pivot;

        return left;

    }

0 0
原创粉丝点击