数据结构之线性表

线性表

1.线性表的定义 :零个或多个数据元素的有限序列

2.抽象数据类型 ==》 结构体

3 顺序存储结构

	定义:用一段地址连续的存储单元依次存储线性表的数据元素

	存储方式:用数组实现,线性表要有数组长度,当前长度。

3.1计算地址的方式

	找到头地址,后面一个加一个单位的地址

3.2.顺序存储结构的插入和删除

3.2.1 获得元素

Status GetElem(SqList L,int i,ElemType *e){

			if(L.length == 0 || i < 1 || i> L.length) return 0;

			*e = L.data[i-1];

			return 1;}




3.2.2插入操作

	思路:看插入位置,位置不合理则返回异常。如果线性表长度大于数组长度了动态增加节点或者返回异常。如果插入位置正常,从最后一个开始向前遍历到第i个位置,依次向后移动一个位置。最后将插入元素放入i位置,线性表长度+1。

	此时传入的是插入元素的值,在原来的地址上面操作值。

Status ListInsert(SqList *L,int i,ElemType e) {

	int k;

	if(L->length == MaxLength ) return 0; //长度达到最大

	if(i < 1|| i > L->length) return 0; //超出下标

	if (i<=L->length) {

		for(k = L->length-1;k >=i-1;k-- ) {

			L->data[k+1] = L->data[k];	

}

}

	L->data[i-1] =e ;

	L->length++;

	return 1;

}






3.2.3删除操作

	思路:条件同上。位置正常的情况下,取出删除的元素,从删除元素位置开始遍历到最后一个元素,依次前移,表长-1。

Status ListDelete(SqList *L,int i,ElemType *e) {

	int k;

	if (L->length == 0) return 0;

	if(i < 1|| i > L->length) return 0;

	*e = L->data[i-1];

	if(i < L->length) {

		for(k = i;k < L->length; k++)   L->data[k-1] = L->data[k];

}

L -> length--;

return 1;

	}

3.3顺序存储结构的优缺点

优点:

	不用考虑逻辑关系增加额外的存储空间

	可以快速查找



缺点:

	删除和插入需要操作大量元素

	元素多的时候难以确定数组长度

4 链式存储结构

定义:节点地址不一定连续,通过指针连接

	    第一个节点的存储位置称为头指针

头指针 

	指向链表中的第一个实际存储数据的节点

	无论链表是否为空,头指针不为空

	通过头指针才能找到链表第一个存储实际元素的节点



头节点 

	在单链表第一个存储目的元素节点前设置的节点,数据域一般无意义(存长度)

	区别第一个节点,头节点不属于链表节点

	不一定为链表必需要素,如果没有头节点,头指针直接指向a1,有就指向头节点,再由头节点的指针指向a1





4.1 单链表获取元素

	1 声明指针p指向第一个节点,初始化j = 1

	2 当j < i 时,遍历链表,p = p->next,j++

	3 当p ==null时,说明第 i 个元素不存在

	4 查找成功就返回p->data



Status GetElem(LinkList L,int i,Elemtype *e) {
	int j;
	LinkList p = L->next;
	j = 1;
	while(p && j < i) {
		p = p->next;
		++j;
}
	if (!p || j > i) return 0;
	*e = p->data;
	return 1;
	}



4.2单链表的插入和删除

	思路:找到对应的插入或者删除位置,进行操作。

		    

	插入 将节点s插入到节点p和p->next之间

	步骤:初始化指针p指向头节点,初始化j = 1

		    当j<i时,遍历链表,让p的指针移动,j++

	            p == null则说明第i个节点不存在

		    

//插入代码核心
s->next = p->next;
p->next = s;

Status ListInsert(LinkList *L,int i,Elemtype e) {
	int j;
	LinkList s , p = *L;
	j = 1;
	while(p && j < i) {
		p = p->next;
		++j;
}
	if (!p || j > i) return 0;
	s = (LinkList)malloc(sizeof(Node));
    s->data = e;
    s->next = p->next;
    p->next = s;
	return 1;
	}

//例如i=3,则是p找到第三个节点,插入元素在第三个节点后面变成第四节点

删除算法的思路

//删除代码 找到删除元素的前驱p,删除p->next                       
p->next = p->next->next;

Status ListDelete(LinkList *L,int i,Elemtype *e) {
	int j = 1;
	LinkList p = *L,q;
	
	while(p && j < i) {
		p = p->next;
		++j;
}
	if (!p || j > i) return 0;
	q = p->next; //这样相对于p->next = p->next->next的好处是可以保留删除节点,后面再释放
    p->next = q->next;
    *e = q->data;
    free(q);
	return 1;
	}









4.3创建单链表

	思路:声明指针p和计数器i;

	初始化空链表L,L->next = null;

	循环部分: 生成新节点赋值给p,随机生成数据赋给数据域,

//头插法
void CreateListHead(LinkList *L,int n) {
		LinkList p;
    	int i;
		srand(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;
        	 p->next = *L->next;
        	 *L->next = p; //插入到表头
    }
}



//尾插法
void CreateListHead(LinkList *L,int n) {
		LinkList p,r;
    	int i;
		srand(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;
        	 r->next = p;
        	 r = p; 
    }
    r->next = null;
}



4.4整表删除

Status ClearList(LinkList *L) {
	LinkList p,q; //一个指针用来标记点位,一个指针用来释放
	p = *L->next;
	while(p) {
		q = p->next; //要记录下当前节点的下一个节点,否则释放了找不到
		free(p);
		p=q;
	}
	*L->next = null;
	return 1;
}



4.5游标实现静态链表

	思路:建立一个大数组,第一和最后一个元素是特殊元素,不做处理,不存数据

		   未存数据的数组元素称为备用链表

		   第一个元素存放备用链表的第一个节点的下标

		   最后一个元素存放第一个有数值元素的下标,通常为1



typedef struct {
	ElemType data;
    int cur;//相当于指针了
} //用这个结构体定义数组,数组有下标,游标,数据域

Status InitList(StaticLinkList space) {
	int i;
    for(i=0;i<MAXSIZE-1;i++) space[i].cur = i+1;//相当于指针的p->next = q;遍历整个链表
    space[MAXSIZE-1].cur = 0; //搜索回头节点,链表为空
    return 1;
}

	插入和删除操作

	思路:模拟动态链表结构的存储空间的分配,需要时申请,无用时释放。封装函数实现

//这个函数可以返回下标值
int Malloc_SSL(StaticLinkList space) {
    int i = space[0].cur; //返回第一个空闲下标
    if(space[0].cur) space[0].cur =  space[i].cur; //被占用了就拿下一个作为备用
    return i;
    //space[i]是找到元素,space[i].cur是找下一个空闲元素
}



//实现下标和游标的交换即可
//把插入的元素下标赋值给第i-1个元素的cur
//把插入元素的cur改成第i个元素的下标,实际上是相互赋值,没有实际的存储顺序改变
Status ListInsert(StaticLinkList L,int i, ElemType e){
	int j,k,l;
     k = MAXSIZE-1;
     if(i<1 || i > ListLength(L)+1) return 0;
    j = Malloc_SSL(L); //获得空闲下标
    if(j) {
		L[j].data = e;  //赋值给空闲的data
         for(l = 1;l <= i-1 ; l++)  k = L[k].cur; //找到第i个元素之前的位置
         L[j].cur = L[k].cur;
                //把插入元素的cur改为第i个元素的下标,此时插入元素变成第i个元素,成功插入
         L[k].cur = j;  
                //把插入元素的下标赋值给第i-1个元素的cur,第i-1个元素可以查找到插入元素
         return 1;
    }
    return 0;
}

	删除操作

void Free_SSL(StaticLinkList space,int k){
	space[k].cur = space[0].cur; //把第一个元素的cur赋值给被删除元素的cur,删除元素的位置可以找到原本备用链表节点
    space[0].cur = k; //被删除的元素作为新的第一个备用链表节点
}

Status ListDelete(StaticLinkList L,int i) {
	int j,k;
	if(i < 1 || i > ListLength(L)) return 0;
	k = MAXSIZE - 1;
	for(j = 1; j <= i-1; j++) k = L[k].cur; //找到被删除元素前一个元素的下标
    
	j = L[k].cur; //
	L[k].cur = L[j].cur; //被删除元素的前一个游标等于被删除元素的游标,被删除元素和它们没有联系了
	Free_SSL(L,j);
	return 1;
}

5 循环链表

循环链表和单链表的主要差异是循环条件的判断

单链表 判断p->next 是否为空

循环链表 判断是否等于头节点

循环链表其实就是把最后一个节点的指针指向头节点即可

合并两个循环链表

p = reaA->next; //保存头节点
reaA->next = reaB->next->next; //越过头节点,指向第一个有意义的元素
reaB->next = p; // 保留一个头节点
free(q);

6 双向链表

双向链表的结构体

typedef struct DulNode {
	ElemType data;
    struct DulNode *prior;
    struct DulNode *next;
} DuNode, *DuLinkList;

p->next->prior = p = p->prior->next

插入操作 将s插入p和p->next

1.先搞定插入节点s的前驱后继

2.再搞定后继节点的前驱

3.最后解决前节点的后继

s->prior = p;
s->next = p->next;
p->next->prior = s;
p->next = s;

删除操作

p->prior p p->next

p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xxxxtrendd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值