ISD1407
---->>C++ 6天
---->>Cocos2d-x ?天
---->>数据结构与算法 ?天
---->>Lua 1天
day13-1 线性表的顺序存储#include <stdio.h>
//声明数组
int array[3] = {1,2,3};
//查看数组中元素的地址
void show(int argc,int argv[]){
for (int i=0; i<argc; i++) {
printf("argv[%d]:%d address:%p\n",i,argv[i],&argv[i]);
}
}
int main(int argc, const char * argv[])
{
show(sizeof(array)/sizeof(array[0]),array);
return 0;
}
打印:
argv[0]:1 address:0x100001058
argv[1]:2 address:0x10000105c
argv[2]:3 address:0x100001060
day13-2 堆内存的分配不是连续的
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[])
{
/*
void *p = malloc(sizeof(int));
int *p2 = (int*)p;类型转换
*/
//在堆中开辟一块内存空间
int *p = (int*)malloc(sizeof(int));
*p = 1;
printf("p value:%p\n",p);//指针指向堆区的地址
printf("p address:%p\n",&p);//指针变量在栈中的地址
//在堆中开辟5个元素的内存空间
int *p1 = (int*)malloc(sizeof(int));
int *p2 = (int*)malloc(sizeof(int));
int *p3 = (int*)malloc(sizeof(int));
int *p4 = (int*)malloc(sizeof(int));
int *p5 = (int*)malloc(sizeof(int));
//查看5个元素的内存地址
printf("p1 value:%p\n",p1);//指针指向堆区的地址
printf("p2 value:%p\n",p2);//指针指向堆区的地址
printf("p3 value:%p\n",p3);//指针指向堆区的地址
printf("p4 value:%p\n",p4);//指针指向堆区的地址
printf("p5 value:%p\n",p5);//指针指向堆区的地址
return 0;
}
打印:
p value:0x10011c0f0
p address:0x7fff5fbff688
p1 value:0x10011c100
p2 value:0x10011c110
p3 value:0x10011c120
p4 value:0x10011c130
p5 value:0x10011c140
day13-3 链表实现方式
#include <stdio.h>
#include <stdlib.h>
//保存多个学生信息
typedef struct _Student{
//值域
int id;
char name[20];
//指针域 描述下一个元素的地址
struct _Student *next;//如果结构体中的类型是当前类型,必须使用原名声明
} Student;
//声明头结点
Student *head = NULL;
//创建一个节点
/*
函数名称:my_malloc
函数参数:void
函数返回值类型:Student * <==>struct _Student *
函数的功能:创建一个节点
*/
Student *my_malloc();
Student *my_malloc(){
//创建一个Student的类型指针
Student *pnew = NULL;
//向堆中申请内存
pnew = (Student*)malloc(sizeof(Student));
if (pnew==NULL) {
printf("分配内存不成功!\n");
return pnew;
}
printf("分配内存成功,请输入节点信息\n");
printf("请输入学员的id信息:\n");
scanf("%d",&pnew->id);//通过结构体指针访问成员使用->符号
printf("请输入学员的姓名信息:\n");
scanf("%s",pnew->name);
//节点的值域创建好了
//单向链表的尾点指向的NULL
pnew->next = NULL;
//节点创建成功
return pnew;
}
//创建链表
/*
函数名称:create_list
函数参数:Student *head头指针
返回值类型:Student *
函数的功能:创建链表
*/
Student *create_list(Student *head){
//指定节点的个数
int n = 0;
//游标 保存临时操作的指针
Student *pnew = NULL;
if (head!=NULL) {
printf("链表已经存在,不需要创建!\n");
//返回当前链表
return head;
}
printf("链表不存在,可以创建\n");
printf("请输入节点的个数:\n");
scanf("%d",&n);
head = my_malloc();//第一节点就是链表的首地址
if (head==NULL) {
printf("表链表头或节点头创建不成功\n");
return NULL;
}
pnew = head;
//循环创建节点 不包括头节点
while (--n) {
//创建新的节点并与上一节点关联在一起
pnew->next = my_malloc();
if (pnew->next==NULL) {
printf("创建新节点失败\n");
return NULL;
}
//如果新节点创建成功,则游标移动到新节点
pnew = pnew->next;
}
printf("链表创建成功!\n");
return head;
}
//遍历链表
/*
函数名称:show_list()
函数参数:Student *head头指针
函数返回值:NULL
函数功能:遍历一个链表中所有节点
*/
void show_list(Student *head){
//创建一个游标
Student *temp = head;
if (head==NULL) {
printf("链表不存在,无法遍历!\n");
//return NULL;
}
//通过游标来遍历链表
while (temp) {
//遍历链表就是输出数据域
printf("id:%d name:%s\n",temp->id,temp->name);
temp = temp->next;//将游标移动到下一节点
}
printf("遍历链表结束");
//return head;
}
int main(int argc, const char * argv[])
{
while(1){
//1.创建链表
printf("1.创建链表\n");
//2.遍历链表
printf("2.遍历链表\n");
//3.销毁链表
printf("3.销毁链表\n");
//用户输入数据
int number = 0;
printf("请输入您所要做的操作:\n");
scanf("%d",&number);
switch (number) {
case 1:
printf("创建链表...\n");
head = create_list(head);
break;
case 2:
printf("遍历链表...\n");
show_list(head);
break;
case 3:
break;
}
}
return 0;
}
打印::
1.创建链表
2.遍历链表
3.销毁链表
请输入您所要做的操作:
数据结构的运算
数据结构与算法(排序算法冒泡)
1.数据结构:是指相互之间具有(存在)一定联系(关系)的数据元素的集合。元素之间的相互联系(关系)称为逻辑结构。数据元素之间的逻辑结构有四基本类型:
集合:结构的数据元素除了”同属于一个集合”外,没有其它关系。
线性结构:结构中的数据元素之间存在一对一的关系。
树型结构:结构中的数据元素之间存在一对多的关系。
图状结构或网状结构:结构中的数据元素之间存在多对多关系。
2.数据结构的存储方式
数据结构在计算机内存中的存储包括数据元素的存储和元素之间的关系的表示。
元素之间的关系 在计算机中有两种不同的表现方法:顺序表示和非顺序表示。由此得出两种不同的存储结构:
顺序存储结构和链式存储结构。
顺序存储结构:用数据元素在存储器中的相对位置来表示数据之间的逻辑结构(关系)。
链式存储结构:每个数据元素增加一个存放另一个元素地址的指针(pointer),用该指针来表示数据元素之间的逻辑结构(关系)。
3.逻辑结构与物理结构
4.数据结构的运算
数据结构的主要运算包括:
(1)建立(Create)一个数据结构
(2)消除(Destroy)一个数据结构
(3)从一个数据结构中删除(Delete)一个元素
(4)把一个数据元素插入(Insert)到数据结构中
(5)对一个数据结构进行访问(Access)
(6)对一个数据结构中的元素进行修改(Modify)
(7)对一个数据结构进行排序(Sort)(算法)
(8)对一个数据结构进行查找(Search)
5.线性表:线性结构是最常用、最简单的一种数据结构。而线性表是一种典型的线性结构。其基本特点是线性表中的数据元素是有序且有限的。在这种结构中:
(1)存在一个唯一的被称为”第一个”的数据元素
(2)存在一个唯一的被称为“最后一个”的数据元素
(3)除第一个元素外,每个元素均有唯一一个直接前驱
(4)除最后一个元素外,每个元素均有唯一一个直接后续
6.线性表的顺序存储
顺序存储结构中,很容易实现线性表的一些操作:初始化、赋值、查找、修改、插入、删除、求长度等。
线性表的顺序存储就是数组。
7.线性表的链式存储
用一组任意的存储单元存储线性表中的数据元素。用这种方法简称线性表。存储链表中的结点的一组任意的存储单元以是连续的或不连续的,甚至是零散分步在内存中的任意位置。
为了正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其直接后继节点的地址(或位置),称为(pointer)或链(link),这两部分级成了链表中的结点结构。
8.树与二叉树
树型结构是一类非常重要的非线性结构。直观地,树型结构是以分支关系定义的层次结构。
树在计算机领域中也有广泛的应用,例如在编译程序中,用树来表示语法结构;在数据库系统中,用树来组织信息;
树(Tree)是n(n>=0)个结点的有限集合T。若n=0时,称为空树,否则:
有且只有一个特殊的称为树的根(Root)结点。
若n>1时,其余的结点被分为m(m>0)个互不相交的子集T1,T2,T3...Tm,其中每个子集本身又是一棵树,称其为根的子树(Subtree)
这是树的递归定义,即用树来定义树,而只有一个结点的树必定仅由根组成。
结点(node):一个数据元素及若干指向其子树的分支。
结点的度(degree)、树的度:结点所拥有的子树的棵数称为结点的度。
二叉树(Binary tree)是n(n>=0)个结点的有限集合。若n=0时称为空树,否则:
有且只有一个特殊称为根(root)结点的。
若n>1时,其余的结点被分为二个互不相交的子集T1,T2,分别称之为左、右子树,并且左、右子树又都是二叉树。由此可知二叉树的定义是迭归的。
顺序存储结构:
用一组地址连续的存储单元依次“自上而下,自左至右”存储完全二叉树的数据元素。
对于完全二叉树上编号为i的结点元素存储一维数组的下标值为i-1的分点中
对于一般的二叉树,将其每个节点与完全二叉树的结点相对照,存储在一维数组中。
遍历二叉树(Traversing Binary Tree)是指按指定的规律对二叉树中的每个节点进行访问一次且仅访问一次。
所谓访问是指对结点做某种处理。如:输出信息、修改结点的值等。
二叉树是一种非线性结构,每个结点都可能有左、右子树,因此需要寻找一种规律,使二叉树的结点能排列在一个线性队列上,从而便于遍历。
二叉树的基本组成:根结点、左子树、右子树。若能依次遍历这三个部分,就是遍历二叉数。
遍历二叉树:
若以L、D、R分别表示遍历左子树、遍历根结点和遍历右子树,则有六种方案:DLR、LDR、LRD、DRL、RDL、RLD。
DLR—先(根)序遍历
LDR—中(根)序遍历
LRD—后(根)序遍历
1408亮哥用C语言敲得代码:
1.Demo_1 顺序存储
// 验证数组的顺序存储
// 数组是线性表的顺序存储 内存地址是连续的。
#include <stdio.h>
//声明一个全局的数组
int a[10] = {0,1,2,3,4,5,6,7,8,9};
int b[4][3] = {{1,2,3},{2,2,3},{3,2,3},{4,2,3}};
//封装一个函数 第一种传数组的方式
void show_fun(int *a,int n);
//封装一个函数 第二种传数组的方式
void show_foo(int a[],int n);
//封装一个函数 传递一个二维数组
void show_arr_arr(int **arr,int m,int n);
//函数实现
void show_fun(int *a,int n)
{
for (int i = 0; i < n; i++)
{
printf("数组元素:a[%d] = 地址:%p ----->数据:%d\n",i,&a[i],a[i]);
}
}
int main(int argc, const char * argv[])
{
//函数中调用一下打印数组的内存地址
show_fun(a, 10);
return 0;
}
2.Demo_2 链式存储(动态内存分配)
// 手动内存管理时
#include <stdio.h>
//第一步需要导入头文件 malloc();
#include <stdlib.h>
int main(int argc, const char * argv[])
{
//第二步 使用malloc函数分配内存 4字节 存 100
int *p = (int *)malloc(sizeof(int));
//分析 p 分配在栈 malloc返回的结果在堆中
//第三步 输出指针变量p,自己的内存地址,和它
//保存的堆内存的地址
printf("p自己在内存中得地址(栈):%p\n",&p);
printf("p中保存的malloc()分配的地址(堆):%p\n",p);
//0x1001000e0这块内存空间中存储一个 整型的数据
*p = 100;
//通过间接寻址运算符 访问这个段内存空间中的值
// 把100 赋值给 *p,也就是p->内存地址
printf("0x1001000e0 地址中存储的数据:%d\n",*p);
//请你使用malloc函数给5个整型变量分配内存地址。
int *p1 = (int *)malloc(sizeof(int));
int *p2 = (int *)malloc(sizeof(int));
int *p3 = (int *)malloc(sizeof(int));
int *p4 = (int *)malloc(sizeof(int));
int *p5 = (int *)malloc(sizeof(int));
//输出 p1--->p5存储的内存地址
printf("p1 = %p\n",p1);
printf("p2 = %p\n",p2);
printf("p3 = %p\n",p3);
printf("p4 = %p\n",p4);
printf("p5 = %p\n",p5);
return 0;
}
3.Demo_3 安全使用malloc
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[])
{
//1.使用malloc()函数 分配内存空间
int *p = (int *)malloc(sizeof(int));
//2.判断 分配是否成功
if(p != NULL)
{
printf("亲~内存分配成功:%p\n",p);
//如果分配成功 继续操作
*p = 200;
}
else
{
printf("内存分配失败!\n");
//异常退出
return -1;
}
return 0;
}
4.Demo6_二维数组做参数
// 二维数组做参数在函数中传递
// 使用二级指针int **p,进行传递,需要
// 注意的是,二维数组在展开时的寻址规律。
#include <stdio.h>
void show(int **arr,int m,int n)
{
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
// 0 1 2 3 先一行的所有列,在换行
//展开的时候要特别注意
printf("%d",*((int *)arr + n*i+j));
}
printf("\n");
}
}
int main(int argc, const char * argv[])
{
int a[4][3] = {{1,2,3},{2,2,3},{3,2,3},{4,2,3}};
int **q = (int **)a;
show(q, 4, 3);
return 0;
}
二、链表
1.链表是什么?线性表的顺序存储 就是数组。线性表的链式存储 就是链表。
2.顺序存储 和链式存储
数据结构中两种常见的存储方式,顺序存储只的是 内存地址在内存中是连续的(数组 保存相同数据类型 的 有序 集合。)链式存储 也就是内存地址在内存有可能是连续的,也有可能是不连续,需要我们借助C语言中内存管理中的 malloc()函数 动态内存分配(内存空间是分配在堆内存中)
3.链表的分类
3.1 单链表(了解)
1.单链表的尾节点 指向 NULL。
2.构成链表的最小单位 节点
1>节点是由两部分组成 数据域 和 指针域
2>数据域 用来存储 节点中的数据
3>指针域 用来存储 后继(下一个)节点的地址(指针)。
3.如何用C语言表示节点的数据类型
struct Node
{
//成员变量
int stu_no;
char name[20]; <==> 数据域
…….
struct Node *next; <==>指针域
};
4.给节点分配内存空间
4.1.C语言的内存管理
1> 静态内存分配
是指由编译器自动为我们分配内存和回收内存,不需要用户手动去处理,这个过程自动完成。
编译器分配的内存空间 一般是栈区,栈空间比较小,gcc编译器为例 10 ~30M左右。
例如:int a = 100; int a[10] = {0};
char string[20] = {0},
struct Student
{
};等
2> 动态内存分配(手动内存管理)
需要程序员 手动向堆内存空间中申请开辟一块
内存空间,来存储我们需要的数据类型。当使
用完毕以后 我们需要手动来释放这块由我们自
己申请的内存空间。
2.1>C语言中内存管理的原则
责任制:谁申请 谁负责释放。
2.2 如何申请内存空间 malloc()函数
1> 导入<stdlib.h>头文件
2> 了解malloc()的用法
函数原型:void *malloc(size_t size);
函数返回值:void *
(*运算,必须强转 有类型的指针类型才
可以 int* char *等)
3> 怎么用?
int *p = (int *)malloc(sizeof(int));
------- ----------------------------------
栈 堆(大,匿名的)
4> 判断(分配内存是否成功)
如果内存分配成功返回一个 堆内存的地
址,如果分配失败返回NULL。
5> 释放我们申请的内存空间
void free(void *ptr);
3.2 双向链表
3.3 循环链表
作业:1.复习 动态内存分配malloc(),free()
2.复习 链表
编程题:(数组 循环 条件判断语句 流程控制语句等)
***1.(算法练习) 请将扑克牌中的54张牌(不要 大王 小王),即剩下的52张牌,随机发给4个玩家,每个玩家13张牌,花色和点数不能重复。
****2.实现中国象棋的棋盘布局(C语言)
2.1不能用printf();
2.2 用数组 用字符串 用函数
车马象士将士象马车
十十十十十十十十十
十炮十十十十十炮十
十十十十十十十十十
卒十卒十卒十卒十卒
十十十十十十十十十
楚河 汉界
十十十十十十十十十
兵十兵十兵十兵十兵
十十十十十十十十十
十炮十十十十十炮十
十十十十十十十十十
車馬相仕帅仕相馬車
1.C语言中的内存管理
1.1 系统级的内存管理
Unix/Mac OSX 64位
物理内存 4G 8G 16G 32G
虚拟内存
页面文件
1.2 用户级别的内存管理
malloc()函数
calloc()函数
作用.处理构造数据类型的内存分配比较多。
realloc()函数
作用:修改malloc()申请的内存空间的大小。
free();
2.指针和结构体的关系
2.1 结构体类型的指针变量
struct Student *point = &liang;
2.2 链表中的节点
1.节点是构成链表的最小单位
2.struct Node
{
int data;//数据域
struct Node *next;//指针域
};
3.链表
3.1 链表的分类 3类
1>单链表 (尾节点指向 NULL)
见链表操作1.0
1.1 链表的创建
1.头插法
2.尾插法
2>双向链表 (又两个指针域 前驱 后继)
3>循环链表 (尾节点 指向 头)
作业:
1.发牌算法
2.中国象棋棋盘
二、栈 队 列 树 排序
特殊的线性表
1.栈是一种特殊的线性表,是一种数据结构。特殊在只允许用户在线性表的一段进行数据的操作。
在栈顶进行数据的操作。是一种 后进先出型的数据结构。(先进后出型的数据结构)
例: 薯片 弹夹(子弹) 前进 后退 撤销 等
栈的分类:1.顺序栈2.链栈
2.队列
是一种特殊的线性表,是一中数据结构。特殊在允许数据从队列的一段进入,从另一端出去。
队列是一种先进先出型的数据结构。
例:洗车排队 银行排队/电信排队/医院排队/QQ消息队列
队列的种类1.顺序队列2.链式存储队列
算法:解决问题的办法
输入输出
有穷性
确定性
可行性
算法的好坏: 时间 空间
时间复杂度
O(n) N级 10个最多通过10次处理得到结果
O(n(2)) 平方级 10个最多通过100次处理得到结果
O(n(m)) 指数级 10个最多通过1000次处理得到结果
O(log(n)) 对数级, 100个数最多处理7次(log128 = 7), 1000个数最多处理10次(log1024=10)
空间复杂度
int year[2014] year[1995] == 1