深入线性表

深入线性表(链表)

一,线性表定义

线性表(List):零个或多个数据元素的有限序列。

有限:0个或者多个数据元素(只有数学概念上才存在无限)

序列:数据元素都是有序的。

​ 元素类型:相同,每个数据元素的类型是相同的。

​ 若将线性表记为(a1, … , ai-1, ai, ai+1, … , an),则表中 ai-1 领先于 ai,ai+1 领先于 ai,称 ai-1 是 ai 的直接前驱元素,ai+1 是 ai 的直接后继元素。当 i = 1,2, … , n-1 时,ai 有且仅有一个直接后继,当 i = 2,3, … , n 时,ai 有且仅有一个直接前驱

在这里插入图片描述请添加图片描述
抽象数据类型ADT

ADT 线性表(List)
    Data
        线性表的数据对象集合为{a1,a2,...,an},每个元素的类型均为 DataType。其中,除第一个元素 a1 外,每个元素有且只有一个直接前驱元素;除了最后一元素 an 之外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。
    Operation
        InitList(*L):初始化操作,建立一个空的线性表 L。
        ListEmpty(*L):若线性表为空,返回 true,否则返回 false。
        ClearList(*L):将线性表清空。
        GetElem(*L,i,*e):将线性表中第 i 个位置元素的值返回给 e。
        LocateElem(*L,*e):在线性表 L 中查找与给定值 e 相等的元素,如果查找成功返回元素在表中的序号表							示成功;否则,返回 0 表示失败。
        ListInsert(*L,i,*e):在线性表 L 中的第 i 个位置插入新元素 e。
        ListDelete(*L,i,*e):删除线性表 L 中第 i 个位置元素,并用 e 返回其值。
        ListLength(*L):返回线性表 L 的元素个数。
endADT

二,线性表的存储结构(顺序,链式)

1.顺序存储结构

顺序表:使用一组地址连续的存储单元依次存储数据元素

​ 小编提示:在编程语言C上不就是**数组**嘛,在java上不就是**ArrayList**嘛,用下标增删改查元素。

​ 特点:

​ 长度固定,必须在分配内存之前确定数组的长度。
​ 存储空间连续,即允许元素的随机访问。
​ 存储密度大,内存中存储的全部是数据元素。
​ 要访问特定元素,可以使用索引访问,时间复杂度为O(1) 。
​ 在顺序表中插入或删除一个元素,都涉及到之后所有元素的移动,因此时间复杂度为O(n)

(1)操作

首先我们要构造线性表
    #define MAXSIZE 200
    typeof int Elemtype;
    typedef struct
    {
    
    
        ElemType data[MAXSIZE];/*创建数组存储数据元素*/
        int length;/*线性表长度*/
    }SqList;

这不就相当于数组+数组长度,所以说顺序存储就是数组。操作线性表,就相当于操作数组

获得元素
首先定义一下返回状态
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 1
typeof int Status;
操作开始:参数有线性表,位置,接收元素的指针
Status GetElem(SqlList L,int i,ElemType *e){
    
    
	//首先应该想什么
	/*当然是找特殊情况了
	 *如果线性表没元素,不能获得元素
	 *如果 i<1或者i>length,同样不能
	 */
	if(L.length<1||i<1||i>L.length){
    
    
		return ERROR;
	}
	//特殊情况之后,就是正常操作了
	*e=L.data[i-1];
	return OK;
}

//O(1)
插入元素
参数e为要插入的元素
Status ListInsert(SqlList *L,int i,ElemType e){
    
    
	//想什么?  对,特殊情况
    /*L满了,i<1,i>length+1*/
    if(L->length==MAXSIZE||i<1||i>L->length+1){
    
    
        return ERROR;
    }
 
    //如果插入位置不是length+1,将第i个元素开始依次向后移动一位
    if(i<=L->length){
    
    
        for(int k = L->length-1;k>=i-1;k--){
    
    
            L->data[k+1]=L->data[k];
        }
    }
    L->length++;
    L->data[i-1]=e;
    return OK;
}
//除了在尾部插入O(1)以外,其他情况O(n)。
删除元素

(注意上边两个方法的参数L,一个是结构体,一个是结构体指针,结构体用. 指针用->)

Status ListDelete(SqlList *L,int i,ElemType *e){
    
    
	/*length<1,i<1,i>length*/
	if(L->length<1||i<1||i>L->length){
    
    
		return ERROR;
	}
	*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 OK;
}
//除了尾部删除都为O(n)

2.链式存储

首先,链表中一个结点包含有一个指针域,就叫单链表;如果包含两个就是双链表。

指向链表的指针叫做头指针head,链表为空:headNULL(沃兹基硕德)
在这里插入图片描述
为了链表好操作,在第一个结点之前加入一个头节点,指针域包含指向第一个节点的指针。
头指针指向头结点,空:head->next
NULL
在这里插入图片描述

定义指针结构体
typedef struct Node{
    
    
    ElemType data;
    struct Node *next;
}Node;//才有点理解C语言这个结构体为什么这样写,将 struct Node{...}起别名为Node
typedef struct Node *LinkList; //将struct Node再起别名为LinkList
/*
 *在方法中创建结点就不用带struct了
 *起别名,结构体具体咋回事我有空再看看C,时间长不用忘了
 */
获得第i个结点的数据
/*有头结点*/
Status GetElem(LinkList L,int i,ElemType *e){
    
    
	//因为不知道L的长度,所以不能直接拿出特殊情况
    if(L->next==NULL||i<1)return ERROR;
    LinkList *p;
    p=L->next;//第一个结点 不能直接用L=L->next;
    while(p){
    
    //如果p没过最后一个结点。因为不知道循环几次用while
        if(i==1){
    
    //走到这就说明找到了。
            *e=p->data;
            return OK;
        }
		//找下一个结点
        p=p->next;
        i--;
    }
    return ERROR;
}
/*
*总结一下:首先特殊情况
*		 其次就是p不为空的while循环
*/
单链表插入

头插法(i=0)

Status ListInsert(LinkList *L,int i,ElemType e){
    
    
	/*特殊情况*/
	
    LinkList p,s;
    p=*L;//头结点
    /*寻找第i-1个元素,在它后面插入*/
    while(p&&i-1>0){
    
    
    	p=p->next;
        i--;
    }
    if(!p||i<1){
    
    //i<1说明插入位置不存在
        return ERROR;
    }
    s=(LinkList)malloc(sizeof(Node))
    s->data=e;
    s->next=p->next;p->next=s;
    return OK;
}
单链表删除
Status ListDelete(LinkList *L,int i,ElemType *e){
    
    
	int j;//用书上的方法,其实一样
    LinkList p,s;
    p=*L;
    j=1;
    //找i-1
    while(p&&j<i-1){
    
    
        p=p->next;
        j++;
    }
    if(!p||!(p->next)||j>i){
    
    //!p->next,第i为NULL,删除位置不对
        return ERROR;
    }
    *e=p->next->data;
    s=p->next;
    p->next=p-next->next;
    free(s);
    return OK;
}

还有特殊的头插法,尾插法。我相信不过多介绍也能写出来。可以去测试一下自己。

return ERROR;
}
*e=p->next->data;
s=p->next;
p->next=p-next->next;
free(s);
return OK;
}


还有特殊的**头插法,尾插法**。我相信不过多介绍也能写出来。可以去测试一下自己。

[LeetCode707. 设计链表]: https://leetcode-cn.com/problems/design-linked-list/

猜你喜欢

转载自blog.csdn.net/weixin_44322227/article/details/121944171