考研408总结【数据结构】---线性表

363 阅读4分钟

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

考研倒计时:43天

参考资料: 王道数据结构考研复习指导 天勤数据结构高分笔记

线性表

线性表.png

定义与特性对比

  1. 线性表的定义 具有相同特性数据元素的一个有限序列

  2. 线性表的逻辑特性 除表头表尾之外,其他元素只有一个直接前驱,也只有一个直接后继。

  3. 线性表的存储结构

顺序存储结构(顺序表)和链式存储结构(链表)

  1. 特性对比

顺序表:随机访问占用连续的存储空间

链表:不支持随机访问,结点的存储空间利用率比顺序表稍低一些,插入操作无需移动元素

顺序表链表
存储密度1<1(结点有指针域)
存储分配一次性分配多次分配
存取方式随机存取或顺序存取顺序存取
  1. 结构体的定义代码
//顺序表
int A[maxSize];
int length = 0;

//单链表
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode;

//双链表
typedef struct DLNode{
    int data;
    struct DLNode *next;
    struct DLNode *prior;
}DLNode;

//使用
LNode *A = (LNode*)malloc(sizeof(LNode));
  1. 注意区分链表的头结点和头指针

插入删除

顺序表

int insertElem(int sqList[],int &length,int p,int &e){
    //函数参数定义为数组,无需加&,其本身为定义的引用型数组
    if(p<0 || p>length || length==maxSize){
        return 0;
    }
    for(int i=length-1;i>=p;i--){
        sqList[i+1] = sqList[i];
    }
    sqList[p] = e;
    ++length;
    return 1;
}
int deleteElem(int sqList[],int &length,int p,int &e){
    if(p<0 || p>length-1){
        return 0;//包括length=0非法条件
    }
    e = sqList[p];
    for(int i=p;i<length-1;i++){
        sqList[i] = sqList[i+1];
    }
    --length;
    return 1;
}

链表

  1. 单链表结点插入
//p结点之后插入s结点
s->next = p->next;
p->next = s;
  1. 单链表结点插入特殊情况

  2. 单链表结点删除

//删除p结点之后的s结点
p->next = s->next;
free(s);
  1. 单链表结点删除特殊情况
  • 给链表设置头结点,可以使得在第一个数据结点之前插入一个新结点和删除第一个数据结点的操作同表中中部结点操作统一起来,方便写代码。
  • 带头结点的链表,其头指针值不随操作而改变,可以减少错误。
  1. 双链表结点插入
//在p之后插入结点s
s->next = p->next;
s->prior = p;
p->next = s;
s->next->prior = s;
  1. 双链表结点删除
//删除p结点之后的s结点
p->next = s->next;
s->next->prior = p;
free(s);

建表

头插法

头指针L指向头结点,创建第一个结点并插入头结点之后、……、创建第i个结点插入头结点之后,如下图所示。

图片来源正念君

image.png

代码如下:

void createLinkListH(LNode *&head){
    head = (LNode*)malloc(sizeof(LNode)); //头结点
    head->next = NULL;                    //此时链表为空
    LNode *p = NULL;                      //新结点
    int n;
    std::cin>>n;
    //创建链表
    for(int i=0;i<n;i++){
        //创建新结点
        p = (LNode*)malloc(sizeof(LNode));
        p->next = NULL;
        std::cin>>p->data;
        //使用头插法把新元素插入链表中
        p->next = head->next;
        head->next = p;
    }
}

如果是双链表,核心代码也是类似

//p是头结点,s是新结点
s->next = p->next;
s->prior = p;
p->next = s;
s->next->prior = s;

尾插法

头指针L指向头结点,创建第一个结点并插入头结点之后、……、创建第i个结点插入第i-1个结点之后。如下图所示。

尾插法与头插法不同的是:尾插法需要创建一个指针始终指向表尾结点。

图片来源正念君

image.png

void createLinkListR(LNode *&head){
    head = (LNode*)malloc(sizeof(LNode));  //头结点
    head->next = NULL;
    LNode *p = NULL, *r = head;            //p指向新结点,r指向表尾结点
    int n;
    std::cin>>n;
    for(int i=0;i<n;i++){
        //创建新结点
        p = (LNode*)malloc(sizeof(LNode));
        p-next = NULL;
        std::cin>>p->data;
        //使用尾插法把新元素插入链表中
        p->next = r->next;
        r->next = p;
        r = p;
    }
}

对于双链表而言,使用尾插法核心代码也是差不多的,只需要在以上基础加一句代码。

        //使用尾插法把新元素插入链表中
        p->next = r->next;
        r->next = p;
        p->prior = r; //!!!多出的一句
        r = p;

逆置

顺序表逆置

不论是奇数个还是偶数个,边界综合条件为i<j

for(int i = left, j = right; i < j; ++i, --j)
{
	temp = a[i];
	a[i] = a[j];
	a[j] = temp;
}

链表逆置

// 逆置p->next到q之间的节点
while(p->next != q)
{
	t = p->next;
	p->next = t->next;
	t->next = q->next;
	q->next = t;
}

相关习题

  1. 将一长度为n的数组的前端k(k<n)个元素逆序后移动到数组后端,要求原数组中数据不丢失
  2. 将一长度为n的数组的前端k(k<n)个元素保持原序移动到数组后端,要求原数组中数据不丢失
  3. 将数组中的元素(X0, X1, … , Xn-1),经过移动后变为(Xp, Xp+1, … , Xn-1, X0, X1, … , Xp-1),即循环左移p(0<p<n)个位置

求最值

不论是链表还是顺序表,扫描一遍进行标记即可。

//线性表求最大值
int max = a[0];
int maxIdx = 0;
for(int i=0; i<n; ++i)
{
	if(max < a[i])
	{
		max = a[i];
		maxIdx = i;
	}
}  

//线性表求最小值
int min = a[0];
int minIdx = 0;
for(int i=0; i<n; ++i)
{
	if(min > a[i])
	{
		min = a[i];
		minIdx = i;
	}
}

//链表求最小值
LNode *p, *q;
int min = head->next->data;
q = p = head->next;
while(p != NULL)
{
	if(min > p->data)
	{
		min = p->data;
		q = p;
	}
	p = p->next;
}

相关习题

一双链表非空,由head指针指出,节点结构为{llink, data, rlink},请设计一个将节点数据域data值最大的那个节点(最大值节点只有一个)移动到链表最前边的算法,要求不得申请新节点空间


部分内容待补充完善~

如有误,请多指正!