码迷,mamicode.com
首页 > 其他好文 > 详细

数据结构:线性表之单链表

时间:2014-10-30 19:14:51      阅读:196      评论:0      收藏:0      [点我收藏+]

标签:数据结构   单链表   

     线性表(亦作顺序表)是最基本、最简单、也是最常用的一种数据结构。线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。线性表有两种存储结构:

①顺序存储结构,即存储单元在一段连续的地址上存储,常见的数组就是顺序存储结构的线性表;
②链式存储结构,即存储单元在不连续的地址上存储。因为其不连续性,除了要存数据元素信息(数据域)外,还要存储它后继元素(结点)的地址(指针域,链)。学习链式结构最好将结点结构牢记于心,如下图:

bubuko.com,布布扣

    链表的每个结点只含有一个指针域就叫做单链表,单链表是其他形式链表以及其他数据结构的基础,所以是这篇文章着重讲的地方。

    在这里还要先提前补充一点知识,时间复杂度与大O计法,正是因为有了这些指标,不同情况下不同数据结构有各自的优越之处。时间复杂度,记作:T(n) = O(f(n)),表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,f(n)是问题规模(代码运行次数)的函数。大O阶计算方法:
①用常数1取代时间中所有的加法常数;
②修改后的运行次数函数f(n)中,只保留最高阶,并去除最高阶前面相乘的常数。
举个例子:
在数组中我们存取数据时,都只需要执行一条语句,f(n)=1,则时间复杂度为O(1);
如果需要插入或删除时,最坏情况(一般都是考虑最坏情况)是在第一个元素插入,则需要把所有元素都向后挪动一位,这样需要作n次处理则f(n)=n,时间复杂度为O(n)。
    最后再介绍两个函数,就可以开始举实例了。
①void *malloc(unsigned int num_bytes) 
动态分配内存指针函数,如果分配成功则返回指向被分配内存的指针(此存储区中的初始值不确定),否则返回空指针NULL。需要进行强制类型转换。
②void free(void *ptr)
释放ptr指向的存储空间。被释放的空间通常被送入可用存储区池,以后可在调用malloc等函数来再分配。


实例:单链表的各种操作,原本想分类一个函数一个函数讲,太乱了,一起摆又太长了,就当是给那些找Demo的人吧!全都是自己写的,后面的也没注释,真的很糟糕~~

#include "stdio.h"
#include "malloc.h"
#include "stdlib.h"

// 一个结点结构体
typedef struct node
{
	int data;  // 数据域,存放数据
	struct node *next;  // 指针域,存放后继结点的地址
}node;

/*
*	功能:创建一个单链表
*	输入:链表长度 
*	输出:头结点的地址
*/
node *createList(int n)
{
	node *h,*p,*s;  // 三个结点指针,指向头结点,前驱结点,当前结点
	h = (node *)malloc(sizeof(node));  // 头结点分配内存空间,返回指针强制转换为结构体指针	
	h->data = 0;  // 头结点数据域一般不用来存放数据,它的存在意义是为了链表增加删除操作的一致性
	p = h;  // 前驱结点设为头结点
	for(int i  = 0; i < n; i++)
	{
		s = (node *)malloc(sizeof(node));  // 分配内存给第一个结点,而后循环
		printf("输入第%d个元素:",i+1);
		scanf("%d",&(s->data));
		p->next = s;  // 将当前结点地址赋给前驱结点的指针域,可以画个图看下
		s->next = NULL; // 当前结点为最后一个结点,指针域赋给NULL
		p = s;  // 前驱结点变为当前结点,即当新建一个结点时,当前结点都会变为他的前驱结点
	}
	return h;  // 返回头结点指针
}

/*
*	功能:得到链表长度
*	输入:链表的头结点
*	输出:整形 
*/
int getSize(node *head)
{
	int size = 0;  
	node *p;  
	p = head;  // 前驱结点赋为头结点
	while(p->next!=NULL)  // 当前驱结点的指针域存放地址,即当前结点,不为NULL时,长度+1
	{
		size++;
		p = p->next;  // 前驱结点要向后移动一位了
	}
	return size;
}

/*
*	功能:得到链表特定位置的数据
*	输入:链表的头结点,获取数据的位置
*	输出:整形 
*/
int get(node *h,int position)
{
	int result = -1;
	node *s,*p;  
	p = h;  
	s = p->next; 
	p = s; 
	for(int i = 0 ; i < position; i++)
	{
		s = p->next;
		p = s;
	}	
	result = s->data;
	return result;
}

/*
*	功能:插入数据
*	输入:链表的头结点,插入的位置,插入的数据 
*	输出:无
*/
void insert(node *h,int position,int data)
{
	node *s,*p;
	s = (node *)malloc(sizeof(node));  
	s->data = data; 
	s->next = NULL;
	p = h;  
	for(int i = 0; i < position; i++)
	{
		p = p->next;  	
	}
	s->next = p->next;   
	p->next = s;  
}

/*
*	功能:动态插入数据在最后一项
*	输入:链表的头结点,插入的数据
*	输出:无
*/
void insertAtLast(node *h,int data)
{
	node *s,*p;
	s = (node *)malloc(sizeof(node)); 
	s->data = data; 
	s->next = NULL;
	p = h;  
	while(p->next!=NULL)
	{
		p = p->next;
	}
	p->next = s;  
}

/*
*	功能:删除数据
*	输入:链表的头结点,删除数据的位置 
*	输出:无
*/
void remove(node *h,int position)
{
	node *p,*s;    
	p = h;
	for(int i = 0; i < position; i++)
	{
		p = p->next;  
	}
	s = p->next;  
	p->next = s->next; 
	free(s);  
}

/*
*	功能:寻找查找数据的第一个地址
*	输入:链表的头结点,所查数据 
*	输出:结点指针
*/
node *search(node *h,int data)
{
	node *p,*s;  
	p = h;  
	while(p->next->data!=data)  
	{
		p = p->next; 
	}
	s = p->next;  
	return s;
}

/*
*	功能:在特定的结点后面插入数据
*	输入:链表的头结点,插入的数据
*	输出:无
*/
void insertAfterPoint(node *point,int data)
{
	node *s,*temp;
	s = (node *)malloc(sizeof(node));
	s->data = data;
	temp = point->next;  
	s->next = temp;  
	point->next = s;  
}

/*
*	功能:删除整个链表
*	输入:链表的头结点 
*	输出:无
*/
void deleteList(node *h)
{
	node *p,*s;
	p = h->next; 
	while(p != NULL)
	{
		s = p->next; 	
		free(p);
		p = s;
	} 
	h->next = NULL;
}

/*
*	功能:打印所有元素的值
*	输入:链表的头结点,链表长度 
*	输出:无
*/
void printAll(node *h,int n)
{
	for(int i = 0 ; i < n; i++)
	{
		printf("%d ",get(h, i));  // 打印链表的每一个值,参数是链表长度  
	}
}

void main(int argc, char* argv[])
{
	node *head;
	head = createList(4);  // 创建一个四个结点的单链表,输入数据1,2,3,4
	printAll(head,getSize(head));  // 1 2 3 4
	printf("\n");
	
	insert(head,1,101);  // 在第二个结点插入101,0对应的是第一个结点 
	insertAtLast(head,102);  // 最后插入102 
	printAll(head,getSize(head));	// 1 101 2 3 4 102
	printf("\n");
	
	remove(head,1);  // 移除最后第二个结点
	printAll(head,getSize(head));  // 1 2 3 4 102 
	printf("\n");	
	
	node *searchPoint = search(head,2);  // 找到数据为2结点的地址
	insertAfterPoint(searchPoint,103);  // 在其后面插入103
	printAll(head,getSize(head)); // 1 2 103 3 4 102
	printf("\n");
	
	deleteList(head);  // 释放链表
	printf("%d",getSize(head)); // 0
	printf("\n");
}

打印结果:

bubuko.com,布布扣

    如果在我们不知道第i个结点的指针位置,单链表数据结构在插入和删除操作上的时间复杂度也为O(n),但是如果要插入10个数据时,我们找到了i个结点的指针位置,插入第一个数据的算法复杂度是O(n),后面的都是O(1),而数组操作则每次都是O(n)。可见:对于插入或删除数据越频繁的操作,单链表的效率优势就越明显。

    总而言之,对于单链表的操作,就是操作头结点,前驱结点,当前结点;前驱结点的指针域数据就是当前结点的指针。

    除了单链表外还有循环链表(终端结点的指针域指向头结点)和双向链表(增加一个前驱结点的指针域),以后有需要时再作深入学习。


数据结构:线性表之单链表

标签:数据结构   单链表   

原文地址:http://blog.csdn.net/leelit/article/details/40622603

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!