zoukankan      html  css  js  c++  java
  • 使用C语言实现线性表

    线性表是最常用且最简单的一种数据结构。一个线性表是n个数据元素的有限序列,序列中的每个数据元素,可以是一个数字,可以是一个字符,也可以是复杂的结构体或对象。例如:1,2,3,4,5是一个线性表,A,B,C,D...Z是一个线性表,一列列车的车厢1,车厢2...车厢n是一个线性表。

    线性表的机内表示法(又称存储结构)有2种,一种是顺序存储结构,另一种是链式存储结构。

    顺序存储结构,顾名思义就是按顺序来存储的一种存储结构,比如线性表(1,2,3,4,5),共计5个元素,
    每个int型的数据元素假设占用4个存储单元,假设第1个元素数字1的存储地址是1000,则第2个元素数字2的存储地址是1004,第3个元素数字3的存储地址是1008,依此类推,第n个数据元素的存储地址是LOC(an) = LOC(a1)+(n-1)k.(k表示每个数据元素占用的存储单元的长度)
    显而易见,这种存储结构,相邻元素在物理位置上也相邻。
    通常,我们把采用这种存储结构的线性表称为“顺序表”。

    链式存储结构,它不要求相邻的元素在物理位置上也相邻,所以它可用一组处在任意位置的存储单元来存储线性表的数据元素。既然这样,那应该怎样表示数据元素之间的逻辑关系呢?
    为了表示数据元素之间的逻辑关系,对于数据元素a1来说,除了存储其自身的信息之外,还需要存储一个指示其直接后继的信息,所以我们引入指针的概念:称存储数据元素信息的域称为数据域,而存储直接后继地址信息的域称为指针域。
    我们形象地称这种即包含自身数据信息,又包含其直接后继地址信息的数据元素为“结点”。
    显而易见,这种存储结构,相邻元素在物理位置上不一定相邻,他们之间用指针来表示逻辑关系。
    通常,我们把采用这种存储结构的线性表称为“线性链表”。

    有了基本的概念之后,我们就可以使用编程语言进行描述,使用C、C++、C#、Java等都可以,这篇文章我使用C语言描述。

    一、顺序表

    为了描述顺序表,我们首先要声明一个结构,如下:

    #define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量
    #define LISTINCREMENT 10   //线性表存储空间的分配增量(当存储空间不够时要用到)
    typedef int ElemType;      //数据元素的类型,假设是int型的
    typedef struct{
    	ElemType *elem;       //存储空间的基地址
    	int length;      //当前线性表的长度
    	int listsize;    //当前分配的存储容量
    }SqList;

    定义好线性表后,就可以对它进行操作了,常见的线性表的基本操作有:创建线性表、查找元素、插入元素、删除元素、清空、归并等。

    线性表的基本操作在顺序表中的实现:
    1.创建线性表

    int InitList(SqList &L)
    {
    	L.elem = (ElemType *)malloc(LIST_INIT_SIZE * sizeof(ElemType));//开辟一个存储空间,并把这块存储空间的基地址赋值给elem
    	if (!L.elem)
    	{
    		return -1; //空间分配失败
    	}
    	
    	L.length = 0; //当前长度
    	L.listsize = LIST_INIT_SIZE; //当前分配量
    	return 0;
    }

    2.查找元素(按值查找)
    线性表的按值查找是指在线性表中查找与给定值x相等的数据元素。完成该操作最简单的方法是从第一个元素a1开始依次和x比较,若相等,则返回该元素的下标。
    若查遍整个表都没有找到与x相等的元素,则返回-1。
    时间复杂度:算法的基本操作(比较x与L中第i<1,n>个元素)与元素x在表中的位置有关,也与表长有关。当a1=x时,比较1次成功,当an=x时,比较n次成功,平均比较次数为n+1/2,时间复杂度为O(n)。

    int LocateElem(SqList L, ElemType x)
    {
    	int pos = -1;
    	for (int i = 0; i < L.length; i++)
    	{
    		if (L.elem[i] == x)
    		{
    			pos = i;
    		}
    	}
    	return pos;
    }

    3.插入元素
    时间复杂度O(L.length)即O(n)

    int ListInsert(SqList &L, int i, ElemType e)
    {
    	//判断插入位置的合法性
    	if (i<1 || i>L.length+1) return -1; 
    	//判断存储空间是否够用
    	if (L.length >= L.listsize)
    	{
    		ElemType *newbase = (ElemType *)realloc(L.elem, (L.listsize + LISTINCREMENT)*sizeof(ElemType));
    		if (!newbase) return -1;//存储空间分配失败
    		L.elem = newbase;//新基址
    		L.listsize += LISTINCREMENT;//增加存储容量
    	}
    	//插入操作
    	ElemType *q, *p; //定义2个指针变量
    	q = &(L.elem[i-1]); //q为插入位置(注意形参i是序号,序号是从从1开始的,而下标是从0开始的,因此这里转成下标后是i-1)
    	for (p = &(L.elem[L.length - 1]); p >= q; --p) //从ai到an-1依次后移,注意后移操作要从后往前进行
    	{
    		*(p + 1) = *p;
    	}
    	*q = e;
    	++L.length;//表长加1
    	return 0;
    }

    4.删除元素
    时间复杂度O(L.length)即O(n)

    int ListDelete(SqList &L, int i, ElemType &e)
    {
    	//判断删除位置的合法性
    	if (i<1 || i>L.length) return -1; 
    	//删除操作
    	ElemType *q, *p;//定义2个指针变量 
    	p = &(L.elem[i - 1]);//p为被删除元素的位置(注意形参i是序号,序号是从从1开始的,而下标是从0开始的,因此这里转成下标后是i-1)
    	e = *p; //被删除的元素赋值给e(可能用不到,也可能用到,所以保存给e吧)
    	q = L.elem + L.length - 1;//q指向表尾最后一个元素(q是最后一个元素的地址)
    	for (++p; p <= q; ++p) //从p的下一个元素开始依次前移
    	{
    		*(p - 1) = *p;
    	}
    	
    	--L.length;//表长减1
    	return 0;
    }

    测试代码如下:

    int main()
    {
    	SqList list;
    	InitList(list);
    
    	int n = 10;
    	
    	//添加10个数字给线性表list
    	for (int i = 0; i < 10; i++)
    	{
    		ListInsert(list, i+1, i+1);
    	}
    	//删除第5个
    	ElemType e;
    	ListDelete(list, 5, e);
    	printf("删除的元素是:%d
    ", e);
    
    	//在第2个位置插入一个元素-1
    	ListInsert(list, 2, -1);
    	
    	//输出线性表
    	for (int i = 0; i < 10; i++)
    	{
    		printf("%d ", list.elem[i]);
    	}
    	//输出结果是:1 -1 2 3 4 6 7 8 9 10
    
    	system("pause");
    }

    二、线性链表

    为了描述线性链表,我们首先要声明一个结构,如下:

    typedef struct LNode{
    	ElemType data; //数据域
    	struct LNode *next;//指针域
    }LNode;

    定义好线性链表后,就可以对它进行操作了,常见的线性链表的基本操作有:查找、插入、删除、清空等。
    线性表的基本操作在线性链表中的实现:
    1.1创建链表(头插法) 时间复杂度O(n)

    LNode * CreateListHead(int n)
    {
    	LNode *L;
    	L = (LNode *)malloc(sizeof(LNode));
    	L->next = NULL;
    	LNode *p;//p为新结点,指向最后一个元素
    	for (int i = n; i > 0; --i)
    	{
    		p = (LNode *)malloc(sizeof(LNode));//为新结点开辟空间
    		scanf("%d",&p->data);
    		p->next = L->next;
    		L->next = p;
    	}
    	return L;
    }

    1.2创建链表(尾插法)

    LNode * CreateListTail()
    {
    	LNode *L;
    	L = (LNode *)malloc(sizeof(LNode));
    	L->next = NULL;//空表
    	LNode *s, *r = L;//r是尾指针 s是新结点
    	int x;
    	scanf("%d", &x);
    	int flag = 0;//结束标记
    	while (x != flag)
    	{
    		s = (LNode *)malloc(sizeof(LNode));
    		s->data = x;
    		r->next = s;
    		r = s;
    		scanf("%d", &x);
    	}
    	r->next = NULL;//最后一个元素next域指向NULL
    	return L;
    }

    2.查找元素(取第i个元素)

    int GetElem(LNode L, int i, ElemType &e)
    {
    	LNode *head,*p;//head是头指针,p为查找下标
    	head = &L;
    	p = head;//p的初值指向第1个元素
    	int j = 0;
    	while (p !=NULL && j<i)
    	{
    		p = p->next;
    		j++;
    	}
    	if (p == NULL || j>i) return -1; //第i个元素不存在
    	e = p->data;
    	return 0;
    }

    3.插入元素
    在链表中插入结点,只需要修改指针。
    若要在第i个结点之前插入元素,则首先需要找到第i-1个结点,然后修改第i-1个结点的指针。
    时间复杂度O(n)

    int ListInsert(LNode *L, int i, ElemType e)
    {
    	LNode *p;//设p为第i-1个结点
    	//首先要找到第i-1个结点
    	int j = 0;
    	p = L;
    	while (p != NULL && j < i-1)
    	{
    		p = p->next;
    		j++;
    	}
    	//申请一个新结点
    	LNode *s = (LNode *)malloc(sizeof(LNode));
    	s->data = e;
    
    	s->next = p->next; //s的直接后继指向p原来的直接后继
    	p->next = s; //p的直接后继指向s
    
    	return 0;
    }

    4.删除元素
    若要删除第i个结点,则只需要修改第i-1个结点的指针指向第i+1个即可
    时间复杂度O(L.length)即O(n)

    int ListDelete(LNode *L, int i, ElemType &e)
    {
    	LNode *p,*q;//设p为第i-1个结点 q标示删除位置
    	//首先要找到第i-1个结点
    	int j = 0;
    	p = L;//p为L的基地址
    	while (p != NULL && j < i-1)
    	{
    		p = p->next;
    		j++;
    	}
    	q = p->next;
    	p->next = q->next;//p的next域指向p->next->next
    
    	e = q->data;
    	return 0;
    }

    测试代码如下:

    int main()
    {
    	LNode *L;
    	L = CreateListTail();//创建一个线性链表,输入0结束
    	ListInsert(L, 1, 100);//在第1个位置插入100
    	ElemType e;//待删除的元素
    	ListDelete(L, 3, e);//删除第3个元素
    	LNode *p;
    	p = L;
    	
    	printf("输出线性链表:
    ");
    	while (p->next != NULL)
    	{
    		p = p->next;
    		printf("%d ", p->data);
    	}
    
    	system("pause");
    }

     笔者初学数据结构,如有不足之处,欢迎指正。

  • 相关阅读:
    java logging 配置文件
    oracle exception使用
    java keytool 用法
    【转】ant学习笔记之(ant执行命令的详细参数和Ant自带的系统属性)
    [转]Ivy入门学习
    关于java.nio.Buffer的API
    如何查看LINUX操作系统是多少位的
    Linux cpio命令的使用
    window.open()使用参考
    【原创】个人站点建设(待续)
  • 原文地址:https://www.cnblogs.com/choon/p/3876606.html
Copyright © 2011-2022 走看看