热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数据结构笔记(郝斌主讲)(2015112622:12:54更新完毕)

教材-课外书籍推荐高一凡(伪算法→真代码)数据结构概述定义我们如何把现实生活中大量而复杂的问题以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在此基础上

教材-课外书籍推荐

高一凡(伪算法→真代码)

数据结构概述
 定义
  我们如何把现实生活中大量而复杂的问题以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在此基础上为实现某个功能(比如查找某个元素,删除某个元素,对所有元素进行排序)而执行的相应操作,这个相应的操作也叫算法

  数据结构 = 个体 + 个体的关系
  算法 = 对存储数据的操作

 算法
  解题的方法和步骤

  衡量算法的标准
   1.时间复杂度
    大概程序要执行的次数,而非执行的时间
   2.空间复杂度
    算法执行过程中大概所占用的最大内存
   3.难易程度

   4.健壮性

 数据结构的地位
  数据结构是软件中最核心的课程

  程序 = 数据的存储 + 数据的操作 + 可以被计算机执行的语言

 最后编辑于2015年8月18日23:10:06


 预备知识
  指针
   指针的重要性:
    指针是C语言的灵魂
   定义
    地址
     地址就是内存单元的编号
     从0开始的非负整数
     范围: 0--FFFFFFFF(0——4G-1)

    指针:
     指针就是地址,地址就是指针
     指针变量是存放内存单元地址的变量
     指针的本质是一个操作受限的非负整数

   分类:
    1.基本类型的指针

    2.指针和数组的关系

 最后编辑于2015年8月19日23:06:51

 结构体
  为什么会出现结构体
   为了表示一些复杂的数据,而普通的基本类型变量无法满足要求

  什么叫结构体
   结构体是用户根据实际需要自己定义的复合数据类型

  如何使用结构体
   两种方式
   struct Student  st = {1000, "zhangsan", 20};
 
   struct Student * pst;

   1.
    st.sid
   2.
    pst->sid
    pst所指向的结构体变量中的sid这个成员

  注意事项
   结构体变量不能加减乘除,但可以相互赋值
   普通结构体变量和结构体指针变量作为函数传参的问题

跨函数使用内存---通过动态内存分配实现
 一般情况下,自定义函数结束后内存释放,唯有动态分配的内存在没有遇见free函数的情况下,自定义函数结束了,但内存仍然存在。

 最后编辑于2015年8月20日23:00:44

模块一:线性结构[把所有的结点用一根直线穿起来]
连续存储[数组]
1. 什么叫数组
元素类型相同,大小相同
2. 数组的优缺点


离散存储[链表]
定义:
n个结点离散分配
彼此通过指针相连
每个节点只有一个前驱节点,每个节点只有一个后续节点
首节点没有前驱结点,尾节点没有后续节点

专业术语:
首节点:
第一个有效节点
尾节点:
最后一个有效节点
头结点:
头结点是数据类型和首结点类型一样
第一个有效节点之前的那个节点
头结点并不存放有效数据
加头结点的目的主要是为了方便对链表的操作
头指针:
指向头结点的指针变量
尾指针:
指向尾节点的指针变量

如果希望通过一个函数对链表进行处理,我们至少需要接受链表的哪些信息:
只需要一个参数:头指针
因为我们通过头指针可以推算出链表的其他所有信息


分类:
单链表
双链表:
每一个节点有两个指针域

循环链表
能通过任何一个节点找到其他所有的结点
非循环链表
算法:
遍历
查找
清空
销毁
求长度
排序
删除节点
插入节点
算法:
狭义的算法是与数据的存储方式密切相关
广义的算法是与数据的存储方式无关
泛型:
利用某种技术达到的效果就是:不同的存储方式,执行的操作是一样的

链表排序算法

1 for(i=0,p=pHead->pNext; i1; ++i,p=p->pNext)
2 {
3 for(j=i+1,q=p->pNext; jpNext)
4 {
5 if(p->data > q->data)//类似于数组中的 a[i] > a[j]
6 {
7 t = p->data;//类似于数组中的:t = a[i];
8 p->data = q->data;//类似于数组中的:a[i] = a[j];
9 q->data = t;//类似于数组中的 a[j] = t;
10 }
11 }
12 }

再论数据结构
  狭义:
    数据结构是专门研究数据存储的问题
    数据的存储包含两方面:个体的存储 + 个体关系的存储
  广义:
    数据结构既包含数据的存储也包含数据的操作
    对存储数据的操作就是算法
算法:
  狭义
    算法是和数据的存储方式密切相关
  广义
    算法和数据的存储方式无关
    这就是泛型思想
数据的存储结构有几种
  线性
  连续存储【数组】
    优点
      存储速度很快
    缺点
      事先必须知道数组的长度
      插入删除元素很慢
      空间通常是有限制的
      需要大块连续的内存块
      插入删除的效率极低

  离散存储【链表】
   优点
    空间没有限制
    插入删除元素很快

   缺点
    存取速度很慢

线性结构的应用--栈

线性结构的应用--队列


非线性


链表的优缺点:

线性结构的两种常见应用之一 栈
定义
一种可以实现“先进后出”的存储结构
栈类似于箱子

分类
静态栈
动态栈

算法
出栈
压栈

应用


线性结构的两种常见应用之二 队列

 

定义:
一种可以实现“先进先出”的存储结构
分类:
链式队列 -- 用链表实现

静态队列 -- 用数组实现
静态队列通常都必须是循环队列

循环队列的讲解:
1.静态队列为什么必须是循环队列吧
2.循环队列需要几个参数来确定
需要2个参数来确定
front
rear


3.循环队列各个参数的含义
2个参数不同场合有不同的含义
建议初学者先记住,然后慢慢体会
1).队列初始化
font和rear的值都是零
2).队列非空
font代表的是队列的第一个元素
rear代表的是队列的有效元素的最后一个有效元素的下一个元素
3).队列空
font和rear的值相等,但不一定是零
4.循环队列入队伪算法讲解

两步完成:
1.将值存入r所代表的位置
2.错误的写法rear = rear + 1;
正确的写法是:rear = (rear + 1) % 数组的长度


5.循环队列出队伪算法讲解

front = (front+1) % 数组的长度
6.如何判断循环队列是否为空

如果front与rear的值相等,
则该队列就一定为空


7.如何判断循环队列是否已满

预备知识:
       front的值可能比rear大,
       front的值也可能比rear小,
       当然也可能两者相等
      两种方式
       1.多增加一个表标识参数
       2.少用一个元素[通常使用第二种方式]
       如果r和f的值紧挨着,则队列已满
       用C语言伪算法表示就是:
       if((r+1)%数组的长度==f)
        已满
       else
        不满

   不满
    队列算法
     入队
     出队
    队列的具体应用:
     所有时间和有关的操作都有队列的影子
 
专题:递归
 定义:
  一个函数自己直接或间接调用自己
 
  递归满足三个条件
   1.递归必须得有一个明确的终止条件
   2.该函数所处理的数据规模必须在递减
   3.这个转化必须是可解的
 
  循环和递归
   递归:
    易于理解
    速度慢
    存储空间大
   循环:
    不易理解
    速度快
    存储空间小
 
 举例:
 
 1. 求阶乘
 2.  1+2+3+4+……100的和
 3. 汉诺塔
 4. 走迷宫

递归的应用:
树和森林就是以递归的方式定义的
树和图的很多算法都是以递归来实现的
很多数学公式就是以递归的方式定义的
斐波那契序列
1 2 3 5 8 13 21 34
逻辑结构
线性
数组
链表
栈和队列是一种特殊的线性结构

非线性


物理结构


模块二:非线性结构

树定义
专业定义:
1.有且只有一个称为根的节点
2.有若干个互不相交的子树,这些子树本身也是一棵树
通俗的定义:
1.树是由节点和边组成
2.每个节点只有一个父节点但可以有多个子节点
3.但有一个节点例外,该节点没有父节点,此节点称为根节点
专业术语
节点 父节点 子节点
子孙 堂兄弟
深度:
从根节点到最底层节点的层数称之为深度
根节点是第一层
叶子节点:
没有子节点的节点
非终端节点
实际就是非叶子节点

子节点的个数称为度

树分类
一般树
任意一个节点的子节点的个数都不受限制
二叉树
任意一个节点的子节点个数最多两个,且子节点的位置不可更改

分类:
一般二叉树
满二叉树
在不增加树的层数的前提下,无法再多添加一个节点的二叉树就是满二叉树

完全二叉树
如果只是删除了满二叉树最底层最右边的连续若干个节点,这样形成的二叉树就是完全二叉树

森林
n个互不相交的树的集合

树的存储
二叉树的存储
连续存储[完全二叉树]
优点:
查找某个节点的父节点和子节点(也包括判断有没有子节点)速度很快
缺点:
耗用内存空间过大

链式存储


一般树的存储
双亲表示法
求父节点方便
孩子表示法
求子节点方便
双亲孩子表示法
求父节点和子节点都很方便
二叉树表示法
把一个普通树转化成二叉树来存储
具体转换方法:
设法保证任意一个节点的
左指针域指向它的第一个孩子
右指针域指向它的兄弟
只要能满足此条件,就可以把一个普通树转化为二叉树
一个普通树转化成的二叉树一定没有右子树

森林的存储

  先把森林转化为二叉树,再存储二叉树

 

操作

 

        遍历

 

            先序遍历

 

                先访问根节点

 

                再先序访问左子树

 

                再先序访问右子树

 


 

 

中序遍历[中间访问根节点]

 

                中序遍历左子树

 

                再访问根节点

 

                再中序遍历右子树
后序遍历[最后访问根节点]
                先中序遍历左子树
                再中序遍历右子树
                再访问根节点

 

 

        已知两种遍历序列求原始二叉树
            通过先序和中序    或者    中序和后序我们可以
            还原出原始的二叉树
            但是通过先序和后序是无法还原出原始的二叉树的
 
已知先序和中序
已知中序和后序

            换种说法:
                只有通过先序和中序,    或通过中序和后序
                我们才可以唯一的确定一个二叉树
链式二叉树简单实现
如图,用程序编写前序遍历,中序遍历,后序遍历
代码如下

1 #include
2 #include<malloc.h>
3
4 struct BTNode
5 {
6 char data;
7 struct BTNode * pLchild; //p是指针&#xff0c;L是左&#xff0c; child是孩子
8 struct BTNode *pRchild;
9 };
10
11 struct BTNode * CreateBTree();
12 void PreTraverseBTree(struct BTNode * pT);
13 void InTraverseBTree(struct BTNode * pT);
14 void PostTraverseBTree(struct BTNode * pT);
15
16 int main(void)
17 {
18 struct BTNode * pT &#61; CreateBTree();
19
20 printf("前序遍历:\n");
21 PreTraverseBTree(pT);//前序遍历
22 printf("中序遍历:\n");
23 InTraverseBTree(pT);//中序遍历
24 printf("后序遍历:\n");
25 PostTraverseBTree(pT);//后序遍历
26
27 return 0;
28 }
29
30 void PostTraverseBTree(struct BTNode * pT)
31 {
32 if(pT !&#61; NULL)
33 {
34
35 if(pT->pLchild !&#61; NULL)
36 {
37 PostTraverseBTree(pT->pLchild);
38 }
39
40 if(pT->pRchild !&#61; NULL)
41 {
42 PostTraverseBTree(pT->pRchild);
43 }
44
45 printf("%c\n",pT->data);
46
47 //pT->pLchild可以代表整个左子树
48 }
49
50
51 /*
52 ·伪算法
53 先访问根节点
54 再先序访问左子树(先序即前序?);
55 再先序访问右子树
56 */
57 }
58
59
60 void InTraverseBTree(struct BTNode * pT)
61 {
62 if(pT !&#61; NULL)
63 {
64
65 if(pT->pLchild !&#61; NULL)
66 {
67 InTraverseBTree(pT->pLchild);
68 }
69
70 printf("%c\n",pT->data);
71
72 if(pT->pRchild !&#61; NULL)
73 {
74 InTraverseBTree(pT->pRchild);
75 }
76
77 //pT->pLchild可以代表整个左子树
78 }
79
80
81 /*
82 ·伪算法
83 先访问根节点
84 再先序访问左子树(先序即前序?);
85 再先序访问右子树
86 */
87 }
88
89
90 void PreTraverseBTree(struct BTNode * pT)
91 {
92 if(pT !&#61; NULL)
93 {
94 printf("%c\n",pT->data);
95 if(pT->pLchild !&#61; NULL)
96 {
97 PreTraverseBTree(pT->pLchild);
98 }
99 if(pT->pRchild !&#61; NULL)
100 {
101 PreTraverseBTree(pT->pRchild);
102 }
103
104 //pT->pLchild可以代表整个左子树
105 }
106
107
108 /*
109 ·伪算法
110 先访问根节点
111 再先序访问左子树(先序即前序?);
112 再先序访问右子树
113 */
114 }
115
116 struct BTNode * CreateBTree()//功能弱&#xff0c;只是静态的
117 {
118 struct BTNode * pA &#61; (struct BTNode *)malloc(sizeof(struct BTNode));
119 struct BTNode * pB &#61; (struct BTNode *)malloc(sizeof(struct BTNode));
120 struct BTNode * pC &#61; (struct BTNode *)malloc(sizeof(struct BTNode));
121 struct BTNode * pD &#61; (struct BTNode *)malloc(sizeof(struct BTNode));
122 struct BTNode * pE &#61; (struct BTNode *)malloc(sizeof(struct BTNode));
123
124 pA->data &#61; &#39;A&#39;;
125 pB->data &#61; &#39;B&#39;;
126 pC->data &#61; &#39;C&#39;;
127 pD->data &#61; &#39;D&#39;;
128 pE->data &#61; &#39;E&#39;;
129
130 pA->pLchild &#61; pB;
131 pA->pRchild &#61; pC;
132 pB->pLchild &#61; pB->pRchild &#61; NULL;
133 pC->pLchild &#61; pD;
134 pC->pRchild &#61; NULL;
135 pD->pLchild &#61; NULL;
136 pD->pRchild &#61; pE;
137 pE->pLchild &#61; pE->pRchild &#61; NULL;
138
139 return pA;
140 }
141

 

//运行如下
快速排序

转:https://www.cnblogs.com/huangtao1996/p/4747544.html



推荐阅读
  • 在高并发需求的C++项目中,我们最初选择了JsonCpp进行JSON解析和序列化。然而,在处理大数据量时,JsonCpp频繁抛出异常,尤其是在多线程环境下问题更为突出。通过分析发现,旧版本的JsonCpp存在多线程安全性和性能瓶颈。经过评估,我们最终选择了RapidJSON作为替代方案,并实现了显著的性能提升。 ... [详细]
  • 本文介绍了一种基于选择排序思想的高效排序方法——堆排序。通过使用堆数据结构,堆排序能够在每次查找最大元素时显著提高效率。文章详细描述了堆排序的工作原理,并提供了完整的C语言代码实现。 ... [详细]
  • 本题要求在一组数中反复取出两个数相加,并将结果放回数组中,最终求出最小的总加法代价。这是一个经典的哈夫曼编码问题,利用贪心算法可以有效地解决。 ... [详细]
  • 主调|大侠_重温C++ ... [详细]
  • 本文深入探讨了MySQL中常见的面试问题,包括事务隔离级别、存储引擎选择、索引结构及优化等关键知识点。通过详细解析,帮助读者在面对BAT等大厂面试时更加从容。 ... [详细]
  • KMP算法是处理字符串匹配的一种高效算法它首先用O(m)的时间对模板进行预处理,然后用O(n)的时间完成匹配。从渐进的意义上说,这样时间复 ... [详细]
  • 本文档汇总了Python编程的基础与高级面试题目,涵盖语言特性、数据结构、算法以及Web开发等多个方面,旨在帮助开发者全面掌握Python核心知识。 ... [详细]
  • cJinja:C++编写的轻量级HTML模板引擎
    本文介绍了cJinja,这是一个用C++编写的轻量级HTML模板解析库。它利用ejson来处理模板中的数据替换(即上下文),其语法与Django Jinja非常相似,功能强大且易于学习。 ... [详细]
  • CSS高级技巧:动态高亮当前页面导航
    本文介绍了如何使用CSS实现网站导航栏中当前页面的高亮显示,提升用户体验。通过为每个页面的body元素添加特定ID,并结合导航项的类名,可以轻松实现这一功能。 ... [详细]
  • 在尝试使用C# Windows Forms客户端通过SignalR连接到ASP.NET服务器时,遇到了内部服务器错误(500)。本文将详细探讨问题的原因及解决方案。 ... [详细]
  • 深入剖析JVM垃圾回收机制
    本文详细探讨了Java虚拟机(JVM)中的垃圾回收机制,包括其意义、对象判定方法、引用类型、常见垃圾收集算法以及各种垃圾收集器的特点和工作原理。通过理解这些内容,开发人员可以更好地优化内存管理和程序性能。 ... [详细]
  • 本文详细介绍了C语言中的基本数据类型,包括整型、浮点型、字符型及其各自的子类型,并探讨了这些类型在不同编译环境下的表现。 ... [详细]
  • 本文详细探讨了 PHP 中常见的 '未定义索引' 错误,包括其原因、解决方案及最佳实践。通过实例和代码片段,帮助开发者更好地理解和处理这一常见问题。 ... [详细]
  • 本文详细介绍了Linux系统中的进程管理函数,涵盖了获取进程ID、用户ID、创建子进程、信号处理等关键操作。通过这些函数,开发者可以更好地控制和管理进程行为。 ... [详细]
  • 本文深入探讨了UNIX/Linux系统中的进程间通信(IPC)机制,包括消息传递、同步和共享内存等。详细介绍了管道(Pipe)、有名管道(FIFO)、Posix和System V消息队列、互斥锁与条件变量、读写锁、信号量以及共享内存的使用方法和应用场景。 ... [详细]
author-avatar
曉--伍_621
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有