看数据结构写代码(4)单链表

单链表比较简单,中间倒也没出什么大问题,只是 在写 插入 和 删除的 算法的 时候 ,时间复杂度 是正常 算法的2倍。后来 改正了。

下面奉上代码。如有 bug,欢迎指出。

// SingleList.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <cstdlib>
enum E_State
{
	E_State_Error = 0,
	E_State_OK = 1,
};
typedef int ElementType;

struct singleList
{
	ElementType data;
	singleList * next;
};

E_State initList(singleList & list){
	list.next = NULL;
	return E_State_OK;
}

E_State destoryList(singleList & list){
	singleList * p = list.next;
	while (p)
	{
		singleList * next = p->next;
		free(p);
		p = next;
	}
	list.next = NULL;
	return E_State_OK;
}

E_State clearList(singleList & l){
	return destoryList(l);
}

bool isEmptyList(singleList l){
	return l.next == NULL ? true : false;
}

int listLen(singleList l){
	singleList * nextList = l.next;
	int len = 0;
	while (nextList)
	{
		nextList = nextList->next;
		len ++;
	}
	return len;
}

E_State getElement(singleList list,int index,singleList & element){
	singleList * listNext = list.next;
	int times = 1;
	for (; times < index && listNext; times ++,listNext = listNext->next);
	if (times > index || !listNext)
	{
		return E_State_Error;
	}
	element = *listNext;
	return E_State_OK;
}

singleList * locateElem(singleList l,ElementType e){
	singleList * next = l.next;
	while (next)
	{
		if (next ->data == e)
		{
			return next;
		}
		next = next->next;
	}
	return NULL;
}

//单链表 查找前驱 比 双向链表 费劲些。
singleList *  preElement(singleList l,ElementType e){
	singleList * headList = l.next;//头指针
	singleList * nextList = headList;
	singleList * lastList = headList;
	while (nextList)
	{
		if (nextList->data == e && nextList != headList)// 头指针没有前驱
		{
			return lastList;
		}
		lastList = nextList;
		nextList = nextList->next;
	}
	return NULL;
}

singleList * nextElement(singleList l, ElementType e){
	singleList * pLocate = locateElem(l,e);
	return pLocate ? pLocate->next:NULL;
}
//后来根据书上代码写的
//插入操作 只要其 前驱不为空即可,可以 从 空表 插入,也可以从 表的尾部的后面插入.
E_State listInsert(singleList & l,int index,ElementType e){
	singleList * p = &l;
	int times = 0;
	//查找插入位置的前驱。。。
	for (; p && times < index - 1; times++,p=p->next) ;
	if (!p || times > index)// times > index 是为了 防止 times 小于 1 的情况..
	{
		return E_State_Error;
	}
	singleList * newNode = (singleList *)malloc(sizeof(singleList));
	newNode->data = e;
	newNode->next = p->next;
	p->next = newNode;
	return E_State_OK;
}

//删除只可删除存在的节点。.
E_State listDelete(singleList & l,int index){
	singleList * next = l.next;
	singleList * last = &l;
	int times = 1;
	for (;times < index && next;)
	{
		last = next;
		next = next->next;
		times ++;
	}
	if (!next && times > index)
	{
		return E_State_Error;
	}
	last->next = next->next;
	//释放 从链表中删除的节点...
	free(next);
	return E_State_OK;
}

/*第一个版本 写的 算法没问题,但是算法复杂度是正常算法的2倍.
E_State listInsert(singleList &l,int index ,ElementType e){
	singleList * next = l.next;
	singleList * last = &l;
	int len = listLen(l);
	if (index < 1 || index > len + 1)
	{
		return E_State_Error;
	}
	int times = 1;
	for (; times < index && next ; times ++,last = next,next = next->next );
	singleList * pNewNode = (singleList *)malloc(sizeof(singleList));
	pNewNode->data = e;
	last->next = pNewNode;
	pNewNode->next = next;
	return E_State_OK;

}

E_State listDelete(singleList &l,int index){
	int len = listLen(l);
	if (index < 1 || index > len)
	{
		return E_State_Error;
	}
	singleList * pNext = l.next;
	singleList * pLast = &l;//这一点很重要。。。
	int times = 1;
	for (;pNext && times < index; )
	{
		pLast = pNext;
		pNext = pNext->next;
		times ++;
	}
	pLast->next = pNext->next;
	//记得释放从链表 去除的 节点
	free(pNext);
	return E_State_OK;
}
*/

void listTraverse(singleList l){
	singleList * next = l.next;
	printf("---------------------------\n");
	while (next)
	{
		printf("%d\n",next->data);
		next = next->next;
	}
	printf("---------------------------\n");
}

int _tmain(int argc, _TCHAR* argv[])
{
	singleList list1;
	//初始化
	initList(list1);
	//插入
	for (int i = 1; i <= 5; i++)
	{
		listInsert(list1,i,i);
	}
	listInsert(list1,3,33);
	listInsert(list1,1,11);
	listInsert(list1,listLen(list1),99);
	listInsert(list1,listLen(list1)+1,100);
	listTraverse(list1);
	//删除
	listDelete(list1,1);
	listDelete(list1,5);
	listDelete(list1,listLen(list1));
	listTraverse(list1);
	//查找,前驱,后继
	singleList  find;
	getElement(list1,5,find);
	singleList * pPre = preElement(list1,find.data);
	singleList * pNext = nextElement(list1,find.data);
	printf("----单链表第五个元素的值是:%d\t,前驱:%d,后继:%d",find.data,pPre->data,pNext->data);
	//释放内存
	destoryList(list1);
	return 0;
}
时间: 2024-10-25 03:00:07

看数据结构写代码(4)单链表的相关文章

看数据结构写代码(6)双向链表的实现

双向链表 只是 比 单链表 多了 一个 指向 前驱的 指针,在插入 和 删除 元素的 时候 得多处理一些.其余 没什么 区别. 而循环链表 的 尾指针 不再 指向 NULL,而是 指向 头指针,这样 既可以循环遍历,又节省 存储空间 . 每种链表 都有 好处,至于 如何 取舍,得看需求. 下面 奉上 双向链表的实现代码: // DoubleLinkList.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <stdlib.h

看数据结构写代码(32) 赫夫曼树编码以及译码

杂谈:最近有点慵懒,不好不好.好几天都没写代码,原本准备上星期完结 树 这一章节的.现在 又耽误了.哎.要抓紧时间啊. 下面直接上代码: 可以到我的网盘下载源代码,或者 直接拷贝下面的源代码 运行 网盘地址:点击打开链接 // HuffmanTree.cpp : 定义控制台应用程序的入口点. //哈弗曼编码,译码 #include "stdafx.h" #include <stdlib.h> #include <cstring> enum E_State { E

看数据结构写代码(21) 稀疏矩阵(十字链表方式)

写完 这个样例,花费了 我不少时间.大部分时间 花费在 调试 内存问题上. 比如在销毁十字链表时.多次释放节点空间,造成 _CrtIsValidHeapPointer(pUserData) 异常. 当使用malloc 分配 一个 空间时,会将这个空间的起始地址和长度 加到一个链表中去.free(p)的时候 ,会从 链表里 查找 是否 有 这个地址空间,找到了就将这个节点从链表中删除._CrtIsValidHeapPointer(pUserData)  这个函数 正是 检查 这个空间是否 在链表里

看数据结构写代码(52) 广义表的扩展线性链表存储表示

广义表 的另一种 存储结构是 扩展线性链表存储表示,这种 存储结构的 根 节点 必 存在,并且 根节点的 表尾 为空,将 根节点的 表尾 放 在 表头 的 表尾 指针上. 这样 从 表头 一直 就可以 遍历 所有 同级 节点. 具体j结构 如下: 例如 下面的 广义表 ,用 扩展线性链表 表示为: 而 头尾 存储表示,是 把 表头 和 表尾 都放在 根节点 的 指针上.其存储结构如下: 所以 其 实现 代码略有 不同,要 小心 处理 下面 上代码: // GList2.cpp : 定义控制台应用

看数据结构写代码(5)静态链表

静态链表用于 不能使用 指针的 编程语言中. 下面奉上代码: // StaticLinkList.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <stdlib.h> //静态链表的 实现 typedef int Element; #define INIT_SIZE 10 //为了测试,故意将值设置的很小 enum E_STATE { E_STATE_ERROR = 0, E_STATE_OK = 1, }; str

看数据结构写代码(37) 图的十字链表的表示与实现

图的邻接表在 查找 有向图的 出度 很 方便,但是 在 查找 入度 时,需要遍历整个图.如果想要 方便的 查找 入度,需要 建立 逆邻接表.十字链表 正好 就是 邻接表 和 逆邻接表的集合.具体结构图如下: 感觉 十字链表 在 查找 入度时 方便 一些,其他 跟 邻接表没什么区别. 源代码 网盘地址:点击打开链接 代码如下: // CrossLinkGraph.cpp : 定义控制台应用程序的入口点. //有向图的十字链表表示法 #include "stdafx.h" #include

看数据结构写代码(60 ) 键树的多重链表表示(Trie树)

trie树,是用 树的 多重链表来表示 树的.每个节点 有 d 个指针域.若从键树中的某个节点到叶子节点的路径上每个节点都只有一个孩子,则可以把 路径上的所有节点压缩成一个叶子节点,且在叶子节点中 存储 关键字 以及 根关键字相关的信息. 当节点的度 比较大时,选择 Trie树,要比 双链表树更为合适. tire树的 数据 压缩 是 挺与众不同的. 下面 给出 具体的 代码: 源代码工程文件网盘地址:http://pan.baidu.com/s/1cyTg6 // TrieTree.cpp :

看数据结构写代码(50)伙伴系统

伙伴系统 是一种 只 可以 分配 2的 幂次方 个 空间的 ,回收 内存 时 只 合并 "伙伴空间" 的一种 动态内存管理方式. 例如 一个 空间 大小 为 64 的 内存,伙伴 系统 为 这 64 的内存  建立 一组 双向循环 链表,分别 管理着  2的 0 次方,2的1 次方幂,2的 2 次方幂...2的6次方幂的 可用空间. 即使 我们 只想分配 一个 大小 为3的 空间,系统 却 只能 返回 一个 内存 大小 为 4(2的2次方)的 一个空间. 系统 在 初始化的 时候 ,并

看数据结构写代码(44) 判断无向图是否有环路

在 看 严蔚敏的 数据结构 一书 7.5小节时,书上 说" 判断有向图是否存在环要不无向图复杂.对于无向图来说,深度优先遍历过程中遇到回边(即指向已访问过的顶点的边),则必定存在环路". 看的不明白,所以 网上 百度了一下. 有了思路:故写下算法 和思路,以便以后 温故. 思路: 1.一个n个顶点,e条边的 无向图,若 e>= n,必有环路. 2.若 e < n ,需要 深度 遍历,并把 父节点传入 参数中,如果 遇到 一个 节点 被访问过 并且 不是 父节点,那么 就有环