数据结构之手撕线性表(有代码注释,持续更新直到完美位置,早看早享受更新)一周更一次,全网最细。

        大纲:

一、线性表及其逻辑结构※

        1,线性表的定义:线性表是具有相同特性的数据元素的一个有限序列。该序列中所含元素的个数叫做线性表的长度(可以看作是一个数组)

       线性表的特性:①有穷性:元素个数是有限的

                                ②一致性:所有元素的性质相同,元素具有相同的数据类型

                                ③序列性:存在唯一的开始元素和终端元素,除此之外,每个元素只有唯一的前驱元素和后继元素,线性表中的位置只取决于它们的序号,所以在一个线性表中可以存在两个值相同的元素。

       2, 线性表的抽象数据类型描述:

        

#include <stdio.h>

//基本运算(个人理解就是一个通用命名规则,考笔试会用到)

InitList(&L){}
//初始化线性表,构造一个空的线性表L,其中参数L是地址

DestroyList(&L){} 
//销毁线性表,释放线性表L所占用的内存空间

ListEmpty(L){}
//判断线性表是否为空表,若L为空表,则返回真,否则返回假

Listlength(L){}
//求线性表的长度,返回L中元素的个数

DispList(L){}
//输出线性表,当线性表L不为空时顺序显示L中各节点的值域

GetElem(L,i,&e){}
//求线性表中某个数据元素值,用e返回L中的第i个元素的值(注意i的取值范围)

LocateElem(L,e){}
//按元素值查找,返回L中第一个值域与e相等的元素的序号,若这样的元素不存在,则返回值为0

ListInsert(&L,i,e){}
//插入数据元素

ListDelete(&L,i,&e){}
//删除数据元素


int main(){


    return 0;
}

二、线性表的顺序存储结构※

        1,顺序表:

线性表的顺序存储结构简称为顺序表(其实就是通过线性表的性质直接映射到存储空间)

                连续存储※:顺序表就是数组,但是在数组的基础上,它还要求数据是从头开始连续存储的,不能跳跃间隔。

        假设线性表的元素类型为E,则每个元素所占用存储空间大小(即字节数)为sizeof(E),(sizeof()为c语言中的一个函数,是求内存空间的大小),整个线性表所占用存储空间大小为  n×sizeof(E)  ,其中   n   就是线性表的长度

         2,顺序表的实现:

                 空间特点:如果满了就不让插入   缺点:给小了不够用,给大了浪费。

                顺序表缺陷:1,空间不够了需要增容,增容是要付出代价的

                                      2,避免频繁扩容,可能导致一定空间的浪费

                                      3,顺序表要求数据从开始位置连续存储,那么我i们在头部或者中间插入删除数据就要挪动数据,效率不高。

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDateType;

//顺序表的实现,动态顺序表
typedef struct SeqList {
	SLDateType* a;
	int size;  //表示数组中存储了多少个数据
	int capacity; //实际能存数据的
	//空间容量(数据个数),也就是最大容量,容量上限
}SL;

//接口函数(这是c++  STL库 的命名规则)

//初始化函数
void SeqListInit(SL* ps){
	ps->a = NULL;
	ps->size = ps->capacity = 0;
};

//打印函数
void SeqListPrint(SL* ps) {

	for (int i = 0; i < ps->size;i++) {
		printf("%d ",ps->a[i]);
	}
	printf("\n");
}

//扩容函数
void SeqListCheckCapacity(SL* ps) {
	//如果没有空间或者空间不足,我们就扩容
	if (ps->size == ps->capacity) {
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;  //扩容
		//如果没有空间就给4个,如果有就扩容2倍
		SLDateType* tmp = (SLDateType*)realloc(ps->a, newcapacity * sizeof(SLDateType));

		//检查扩容是否成功
		if (tmp == NULL) {
			printf("realloc 扩容失败");
			return(-1);
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
}
//尾部插入数据:
void SeqListPushBack(SL* ps,SLDateType x) {
	SeqListCheckCapacity(ps);
	ps->a[ps->size] = x;  
	//这个并不叫做指针数组。
	//a是一个指向int的指针,它指向一个动态分配的int数组。
	//使用malloc或calloc为a分配内存时
	//您实际上是在创建一个整数数组
	//而a是指向这个数组首元素的指针
	ps->size++;
		
};

//对空间进行销毁
void SeqListDestory(SL* ps) {
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->size = 0;

}

//在尾部删除数据
void SeqListPopBack(SL* ps) {
	//ps->a[ps->size - 1] = 0;其实不用清空数据
	//直接size--就行,因为打印是用size控制的
	//只需要控制打印的长度就行
	assert(ps->size > 0); //断言判断是否越界
	ps->size--;
};

//头部插入
void SeqListPushFront(SL* ps, SLDateType x) {
	//判断空间是否足够,是否需要扩容
	SeqListCheckCapacity(ps);
	//挪动数据
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		//例如下长度是4,那么数组下标就是0,1,2,3
		//要把3往后面挪动,那就是第4个位置的值等于第三个位置的值
		--end;
	}
	ps->a[0] = x;//把要插入的值插入到头部,也就是下标为0的位置
	ps->size++;//长度增加1,因为插入了一个新数据
};

//头部删除
void SeqListPopFront(SL* ps) {
	assert(ps->size > 0);
	//其实可以倒着打印,然后--size就可以了哈哈啊哈。
	//挪动数据
	int begin = 1;
	while (begin < ps->size) {
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}
	ps->size--;
};


//查找,找到了返回x位置下标 ,没有找到返回-1
int SeqListFind(SL* ps,SLDateType x) {
	for (int i = 0; i < ps->size; i++) {
		if (ps->a[i] == x) {
			return i;
		}
	}
	return -1;
}

//在指定下表位置(pos)插入值
void SeqListInsert(SL* ps,int pos,SLDateType x){
	assert(pos >= 0 && pos <= ps->size );//判断插入的位置是否正确
	//考虑空间
	SeqListCheckCapacity(ps);
	int end = ps->size - 1;//下标所以要减1
	//挪动数据
	while (end >= pos) {
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	//插入数据
	ps->a[pos] = x;
	ps->size++;

}

//在指定的位置(pos)删除指定的数据
void SeqListErase(SL* ps, int pos) {
	//判断删除的位置是否正确
	assert(pos >= 0 && pos <= ps->size);
	int begin = pos + 1;
	while (begin < ps->size) {
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}
	ps->size--;
	//如果是删除最后一个元素
	//跳出循环,直接把长度减一就好了
}

//text测试函数1
void TestSeqList1() {
	SL sl;   //定义结构体
	SeqListInit(&sl);//调用初始化函数 传地址
	SeqListPushBack(&sl, 1);//尾插入
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	//打印值
	SeqListPrint(&sl);
	
	//删除测试
	SeqListPopBack(&sl);  
	SeqListPopBack(&sl);
	SeqListPrint(&sl);//打印值



	//最后对空间进行销毁
	SeqListDestory(&sl);
}

//text测试函数2
void TestSeqList2() {
	SL sl;   //定义结构体
	SeqListInit(&sl);//调用初始化函数 传地址
	SeqListPushBack(&sl, 1);//尾插入
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	
	//头部插入
	SeqListPushFront(&sl, 10);
	SeqListPushFront(&sl, 20);
	SeqListPushFront(&sl, 30);
	SeqListPushFront(&sl, 30);
	SeqListPushFront(&sl, 30);
	//打印值
	SeqListPrint(&sl);
	
	//头部删除
	SeqListPopFront(&sl);
	//打印值
	SeqListPrint(&sl);
	//最后对空间进行销毁
	SeqListDestory(&sl);
}

//text测试函数3
void TestSeqList3() {
	SL sl;   //定义结构体
	SeqListInit(&sl);//调用初始化函数 传地址
	SeqListPushBack(&sl, 1);//尾插入
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	//打印值
	SeqListPrint(&sl);
	//第二个位置插入值30
	//SeqListInsert(&sl,2,30);
	//打印值
	SeqListPrint(&sl);
	//删除指定位置的元素
	SeqListErase(&sl,5);
	SeqListPrint(&sl);
	//最后对空间进行销毁
	SeqListDestory(&sl);
}


//主函数
int main() {

	//TestSeqList1();//测试1
	//TestSeqList2();//测试2
	TestSeqList3();//测试3
	printf("测试");
	return 0;
}

三、线性表的链式存储结构※

        针对顺序表的缺陷,设计出的链表。

1,单链表:

注意二级指针

#include <stdio.h>
#include <stdlib.h>


//单链表
typedef int SLTDateType;

//定义结构体类型
typedef struct SListNode {
	SLTDateType data;
	struct SListNode* next;
}SLTNode;

//打印链表
void SListprint(SLTNode* phead) {
	SLTNode* cur = phead;
	while (cur != NULL) {
		
		printf("%d ", cur->data);
		cur = cur->next;
	}
}

void SListPushBack(SLTNode** pphead,SLTDateType x) {
	//开辟空间newnode节点
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	newnode->data = x;
	newnode->next = NULL;
	//如果穿过来的指针为空,说明这个是尾节点,把新节点newnode进行链接
	if (*pphead == NULL) {
		*pphead = newnode;
	}
	else {
		//如果不是从传过来的节点一直往下找到节点的next为空为止
		SLTNode* tail = *pphead;
		while (tail->next != NULL) {
			tail = tail->next;
		}
		//将tail链接到下一节点地址也就是刚刚开辟的新节点newnode
		tail->next = newnode;
	
	}
	
}

void TestSList1() {
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);

	SListprint(plist);
}


int main() {

	TestSList1();
	
	return 0;
}

四、线性表的应用

五、有序表

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值