《大话数据结构》--学习笔记9 ***重点***
2012-05-15 11:48
281 查看
3.9 单链表的整表创建
单链表和顺序存储结构的区别:1.单链表不像顺序存储结构这么集中,它可以很散,是一种动态结构;
2.对每个链表来说,它所占用空间的大小和位置是不需要预先分配划定的,可以根据系统的情况和实际的需求即时生成。
所建立单链表的过程就是一个动态生成链表的过程。即从“空表”的初始化状态起,依次建立各元素结点,并逐个插入链表。
单链表整表创建的算法思路:
1.声明一个结点p和计数器变量i;
2.初始化一空链表L;
3.让L的头结点的指针指向NULL,即建立一个带头结点的单链表;
4.循环:
& 生成一新结点赋值给p;
& 随机生成一数字赋值给p的数据域p->data;
& 将p插入到头结点与前一新结点之间;
实现单链表整表创建代码算法如下:
/*随机产生n个元素的值,建立带表头结点的单链线性表L(头插法)*/
void CreateListHead (LinkList *L, int n ) { LinkList p; int i; strand (time(0)); /*初始化随机数种子*/ *L = ( LinkList ) malloc (sizeof(Node)); (*L)->next =NULL; /*先建立一个带头结点的单链表*/ for (i=0; i<n; i++) { p = (LinkList ) malloc (sizeof(Node)); /*生成新结点*/ p->data = rand () %100 +1; /*随机生成100以内的数字*/ p->next = (*L)->next; /*即NULL结尾*/ (*L)->next =p; /*插入到表头*/ } }
这段算法代码里,我们其实用的是插队的办法,就是始终让新结点在第一的位置。我们可以把这种算法简称为头插法。如图:
可事实上,我们还是可以不这样干,为什么不把新结点都放最后呢?这才是排队的正常思维,所谓先来后到。
我们把每次新结点都插在终端结点的后面,这种算法称之为尾插法。
实现尾插法代码算法如下:
/*随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法)*/
void CreateListTail (LinkList *L, int n ) { LinkList p,r; int i; strand (time(0)); /*初始化随机数种子*/ *L = ( LinkList ) malloc (sizeof(Node)); /*为整个线性表*/ r =* L; /*r为指向尾部的结点*/ for (i=0; i<n; i++) { p = (Node*) malloc (sizeof(Node)); /*生成新结点*/ p->data = rand () %100 +1; /*随机生成100以内的数字*/ r->next = p; /*将尾端结点指针指向新结点*/ r =p; /*将当前的新结点定义为尾端结点*/ } r->next = NULL; /*表示当前链表结束*/ }
注意L与r的关系,L是指整个单链表,而r是指向尾结点的变量,r会随着循环的不断变化结点,而L则是随着循环增长为一个多结点的链表。
这里需要解析一下,r->next=p 是将刚才的表尾端结点r的指针指向新结点p; (难点:) r =p就不是很好理解了,是什么意思?看图:
它的意思是,就是本来r是在ai-1元素的结点,可现在它已经不是最后的结点了。(其实r就是一个尾节点的标志,当r没有在尾时,必须把它移到尾)
循环结束,那么应该让这个链表指针域置空,因此有了“r->next=NULL”以便以后遍历时可以确认其尾部。
个人思考附加:
附加一:
头插法和尾插法的区别:
头插法:头指针装NULL,每次从后面添加元素,NULL尾都要往后挪一个地方;
尾插法:头指针没有装NULL,每次从后面添加元素,添加完后,再在尾指针装NULL;
附加二:
srand函数是随机数发生器的初始化函数。
原型:void srand(unsigned seed);
用法:它需要提供一个种子,这个种子会对应一个随机数,如果使用相同的种子后面的rand()函数会出现一样的随机数。如: srand(1); 直接使用1来初始化种子。不过为了防止随机数每次重复常常使用系统时间来初始化,即使用 time函数来获得系统时间,它的返回值为从 00:00:00 GMT, January 1, 1970 到现在所持续的秒数,然后将time_t型数据转化为(unsigned)型再传给srand函数,即: srand((unsigned) time(&t)); 还有一个经常用法,不需要定义time_t型t变量,即:
srand((unsigned) time(NULL)); 直接传入一个空指针,因为你的程序中往往并不需要经过参数获得的t数据。
函数一:int rand(void);
从srand (seed)中指定的seed开始,返回一个[seed, RAND_MAX(0x7fff))间的随机整数。
函数二:void srand(unsigned seed);
参数seed是rand()的种子,用来初始化rand()的起始值。
可以认为rand()在每次被调用的时候,它会查看:
1) 如果用户在此之前调用过srand(seed),给seed指定了一个值,那么它会自动调用
srand(seed)一次来初始化它的起始值。
2) 如果用户在此之前没有调用过srand(seed),它会自动调用srand(1)一次。
根据上面的第一点我们可以得出:
1) 如果希望rand()在每次程序运行时产生的值都不一样,必须给srand(seed)中的seed一个变值,这个变值必须在每次程序运行时都不一样(比如到目前为止流逝的时间)。
2) 否则,如果给seed指定的是一个定值,那么每次程序运行时rand()产生的值都会一样,虽然这个值会是[seed, RAND_MAX(0x7fff))之间的一个随机取得的值。
3) 如果在调用rand()之前没有调用过srand(seed),效果将和调用了srand(1)再调用rand()一样(1也是一个定值)。
rand () %100 即随机生成100以内的数;
3.10 单链表的整表删除
当我们不打算使用这个单链表时,我们需要把它销毁,其实也就是在内存中将它释放掉,以便留出空间给其他程序员或软件使用。单链表的整表删除的算法思路如下:
1.声明一结点p和q;
2.将第一个结点赋值给p;
3.循环:
& 将下一结点赋值给q;
& 释放p;
& 将q赋值给p;
实现单链表的整表删除代码算法如下:
/*初始化条件:顺序线性表L已存在,操作结果:将L重置为空表*/
Status ClearList (LinkList *L) { ListList p ,q; p = (*L)->next; /*p指向第一个结点*/ while (p) /*没到表尾*/ { q = p->next; /*一个个释放*/ free (p); p = q ; } (*L)->next =NULL; /*头结点指针域为空*/ return OK; }
这段代码里,常见的错误就是由同学会觉得q变量没有存在的必要。在循环体内直接写free(p);p=p->next ;即可,可这样带来什么问题?
要知道p是一个结点,它除了有数据域,还有指针域。你在做free(p)时,其实时在对它整体结点进行删除和内存释放工作。
怎么说呢?你看懂《大话数据结构》--学习笔记9 ***重点*** 尾插法中的那句:“r=p” 了吗?如果看懂了 ,这个也应该没问题的。因为他们类似的,都起到一个指针传递的作用。
相关文章推荐
- 《大话数据结构》--学习笔记10 ***重点***
- 《大话数据结构》--学习笔记12 ***重点***
- 《大话数据结构》--学习笔记8 ***重点***
- 《大话数据结构》--学习笔记11 ***重点***
- 本文章总结C++学习重点,与笔记
- Java学习笔记(必看经典)(已作标记,红色部分为重点)
- Oracle 学习笔记 查询(重点,也是基础)
- jQuery学习笔记--JqGrid相关操作 方法列表 备忘 重点讲解(超重要) from:jpr1990
- 黑马程序员之 ASP.NET学习笔记:Server.Excute和Server.Transfer重点讲解
- 《大话数据结构》第一章学习笔记
- 黑马程序员---OC学习笔记之分类(Category)非正式协议【重点】
- J2SE学习笔记:J2SE重点难点,第一讲String
- jQuery学习笔记--JqGrid相关操作 方法列表 备忘 重点讲解(超重要)
- jQuery学习笔记--JqGrid相关操作 方法列表 备忘 重点讲解(超重要)
- Hibernate学习笔记总结(三)——Hibernate 3.x——HQL查询语言(重点)
- Andrew Ng机器学习课程笔记--week11(图像识别&总结划重点)
- IOS学习笔记之十:IOS重点内容 Delegate的理解
- 《大话数据结构》学习笔记--chapter 4
- 学习笔记【机器学习重点与实战】——3 决策树
- PHP:面向对象学习笔记,重点模拟Mixin(掺入)