IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 考研数据结构手记(2)-线性表 -> 正文阅读

[数据结构与算法]考研数据结构手记(2)-线性表

第二章 线性表

2.1大纲要求

(一)线性表的基本概念
(二)线性表的实现
 ?1.顺序存储
 ?2.链式存储
(三)线性表的应用

2.2 线性表的基本概念

线性表的定义:线性表是具有相同特性元素的一个有限序列
线性表的存储结构:顺序存储和链式存储,也就是顺序表和链表,
?????????顺序表的定义定义顺序表
?????????链表的定义
在这里插入图片描述

存储结构逻辑上物理地址
顺序表相邻相邻
链表相邻不一定相邻

Head指针的作用是完整的标识了整个链表
在这里插入图片描述

单链表的判空条件是:
有头结点的单链表:Head->next = NULL
无头结点的单链表:Head = NULL

双链表
在这里插入图片描述
与单链表的区别就是给每个结点增加了指向前驱的指针。
判空条件与单链表相同。

循环单链表
在这里插入图片描述

循环单链表的判空条件是:
有头结点的循环单链表:Head->next = Head
不带头结点的循环单链表:Head = NULL

循环双链表:
在这里插入图片描述

循环双链表的判空条件是:
有头结点:Head->next=Head或Head->prior=NULL
不带头结点的循环单链表:Head = NULL

习题一
在这里插入图片描述

#include<iostream>
#include<cmath>
using namespace std;
#define MIN 0.001
#define MAXSIZE 100
int compare(float A[],int alen,float B[],int blen)
{
    int i = 0;
    while(i < alen && i < blen)
    {
        if (fabs(A[i]-B[i])<MIN)
            ++i;
        else
            break;
    }
    if(i >= alen && i >= blen)
        return 0;
    else if((i>=alen&&i<blen) || A[i]<B[i])
        return -1;
    else
        return 1;
}
int main()
{
    float A[MAXSIZE] = {1,2,3,4};
    float B[MAXSIZE] = {1,2,3,4,5,6};
    cout <<compare(A,4,B,6);
    return 0;
}

2.3 存储结构特性对比

2.3.1 插入删除元素

2.3.1.1 顺序表插入

选定位置后,从后往前所有元素依次向右移动一个位置。从前往后会造成元素的覆盖。

2.3.1.2 顺序表删除

选定位置后,从前往后所有元素依次向左移动一个位置。从后往前会造成元素的覆盖。

2.3.1.3 结论

顺序表插入删除可能会导致大量的元素的连带操作,而链表不会。

2.3.2 取元素

在单链表中找到任意一个结点的位置不像顺序表那么简单,因为顺序表支持随机存储,而链表不支持。

2.3.3 存储空间

线性表采用顺序存储结构,必须占用一整片连续的存储空间,而采用链式存储结构则不需要这样;
从表的整体来看,一般顺序表存储空间利用率低于链表,但从单个的存储单元来看,顺序表的利用率高于链表。
存储密度是指结点本身所占的存储量和整个存储结构所占的存储量之比。
习题二
在这里插入图片描述

选A,因为顺序表支持随机存储

在这里插入图片描述

选B,排除法

在这里插入图片描述

2.4 元素移动次数计算和静态链表

2.4.1 元素移动次数计算

主要应用于顺序表的插入和删除元素操作。
一个长度为n的顺序表可供插入的位置为n+1,所以在任意位置插入元素的概率为: 1 n + 1 \frac{1}{n+1} n+11?
在i位置前插入元素,需要移动n-1个元素;
所以插入元素的平均移动次数为 n 2 \frac{n}{2} 2n?
一个长度为n的顺序表可供插入的位置为n+1,所以在任意位置删除元素的概率为: 1 n \frac{1}{n} n1?
在i位置前插入元素,需要移动n-i-1个元素;
所以插入元素的平均移动次数为 n ? 1 2 \frac{n-1}{2} 2n?1?

2.4.2 静态链表

静态链表结点类型定义:

typedef struct
{
	int data;
	int next;
}SLNode;

在这里插入图片描述
构造一个长度为MAXSIZE的静态链表:

SLNode SLink[MAXSIZE];

在这里插入图片描述

int p = Ad0; //定义一个指针
SLink[p].data;//取p指向的结点值,相当于p->data
SLink[p].next;//取p的后继节点指针,相当于p->next
在p后插入结点q:
SLink[q].next = SLink[p].next;
Slink[p].next = q;
//类比 q.next = p.next p.next = q

习题三:
在这里插入图片描述

选B
A.两个都是O(1)
C.结点数目相同的链表占用存储空间相同

在这里插入图片描述

选C
A.并没有简单
B.不支持随机存储
D.不能很好的利用零散存储空间

2.5 线性表插入删除

2.5.1 单链表插入删除元素

2.5.1.1 插入

在这里插入图片描述
实现代码:

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

2.5.1.2 删除

在这里插入图片描述
实现代码:

p->next = s->next;
free(s);

在这里插入图片描述

2.5.2 双链表插入删除元素

2.5.2.1 插入

在这里插入图片描述
实现代码:

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

2.5.2.2 删除

在这里插入图片描述
实现代码:

s->prior->next = s->next 
s->next->prior = s->prior
free(s);

2.5.3 顺序表元素插入删除

在这里插入图片描述

2.5.3.1 插入元素

可插入元素的位置:0-length;
当length=maxsize时不可以再插入元素;
移动元素从前往后进行。

插入操作实现代码:

int sqList[maxsize] = {1,2,3,4,...n};
int length = n;
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;
}

2.5.3.2 删除元素

可插入元素的位置:0-length-1;
当length=0时不可以再插入元素;
移动元素从后往前进行。

删除操作实现代码:

int sqList[maxsize] = {1,2,3,4,...n};
int length = n;
int DeleteElem(int sqList[],int &length,int p,int e)
{
    if(p<0||p>length-1)
        return 0;
    e = sqList[p];
    for(int i = p;i<length-1;++i)
    {
        sqList[i]=sqList[i+1];
    }
    --length;
    return 1;
}

习题四:
在这里插入图片描述

选B,链表长度越长,需要扫描的数据就越多

在这里插入图片描述

void DeleteElem(int sqList[],int &length,int p,int e)
{
    int len = j-i+1;
    for(int k = j+1;k<length;++i)
    {
        sqList[k-len]=sqList[k];
    }
    length-=len;
}

在这里插入图片描述

void del(LNode *L)
{
	LNode *p = L-next,*q;
	while(p->next !=NULL)
	{
		if(p->data==p->next-data)
		{
			q = p->next;
			p->next = q->next;
			free(q);
		}
		else
		{
			p = p->next;
		}
	}
}

在这里插入图片描述

void spilit(LNode *A,LNode *B)
{
	B = (LNode*)molloc(sizeof(LNode));
	LNode *p,*q,*r;
	B->next = NULL;
	r = B;
	p = A;
	while(p->next!=NULL)
	{
		if(p->next->data%2==0)
		{
			q = p->next;
			p->next= q->next;
			q->next =NULL;
			r->next =q;
			r = q;
		}
		else
		{
			p = p->next;
		}
	}
}

2.6 建表

2.6.1 顺序表建表

实现代码:
在这里插入图片描述

2.6.2 链表建表

实现代码(尾插法):
在这里插入图片描述
实现代码(头插法):
在这里插入图片描述
习题五:
在这里插入图片描述
在这里插入图片描述

2.7 逆置问题

2.7.1 顺序表逆置

在这里插入图片描述
主要实现代码:

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

2.7.2 链表逆置

在这里插入图片描述
主要实现代码:

t = p->next;
p->next = t->next;
t->next = q->next;
q->next = t;
if(p->next ==q)
结束;

习题六:
在这里插入图片描述

1.算法思想,先将R中前P个元素逆置,再将剩下的元素逆置,最后将整个R逆置即可。

2.void reverse(int a[],int left,int right,int p)
{
	for(int i = left,int j = right;i<left+p&&i<j;++i,--j)
	{
		temp = a[i];
		a[i] = a[j];
		a[j] = temp;
	}
}
void solve(int a[],int n,int p)
{
	reverse(a,0,p-1,p);
	reverse(a,p,n-1,n-p);
	reverse(a,0,n-1,n);
}

3.时间复杂度:O(n);
空间复杂度:O(1);

原地算法:辅助空间相对于输入数据量而言是个常数的算法

2.8 取最值

2.8.1 顺序表求最值

在这里插入图片描述
主要实现代码:
在这里插入图片描述
最小值改一下细节即可;

2.8.2 链表取最值

主要实现代码:

LNode *p,*q;
int max = head->next->data;
q = p = head->next;
while(p!=NULL)
{
	if(max < p->data)
	{
		max = p->data;
		q = p;
	}
	p = p->next;
}

在这里插入图片描述
习题七:
在这里插入图片描述

实现代码:
void maxFirst(DLNode *head)
{
	## get max 
	DLNode *p = head->rlink,*q = p;
	int max = p->data;
	while(p!=NULL)
	{
		if(max<p->data)
		{
			max = p->data;
			q = p;
		}
		p = p->rlink;
	}
	----------------------------------------
	##delete
	DLNode *l = q->llink,*r = q->rlink;
	l->rlink = r;
	if(r!=NULL)
		r->llink = l;
	----------------------------------------	
	##insert
	q->llink = head;
	q->rlink = head->rlink;
	head->rlink = q;
	q->rlink->llink = q;
}

在这里插入图片描述

1.算法基本思想:
(1)分别求出两个链表的长度,len1,len2;
(2)令指针p、q分别指向str1和str2的头结点,若m>=n,则使p指向链表中的第m-n+1个结点;若m<n,则使q指向链表中的第n-m+1个结点;即使指针p和q所指的结点到表尾的长度相等。
(3)将指针p和q同步向后移动,并判断它们是否指向同一结点。若p和q指向同一结点,则该点即为所求的共同后缀的起始位置。

2.LNode *solve(LNode *str1,LNode *str2)
{
	int len1 = 0,len2 = 0;
	LNode *p = str1->next,*q = str2->next;
	while(p!=NULL)
	{
		len1++;
		p = p->next;
	}
	while(q!=NULL)
	{
		len2++;
		q = q->next;
	}
	for(p = str1->next;len1>len2;len1--)
		p = p->next;
	for(q = str2->next;len1<len2;len2--)
		q = q->next;
	while(p!=NULL&&p!=q)
	{
		p = p->next;
		q = q->next;
	}
	return p;
}

3.时间复杂度为O(n)

2.9 划分

给一个顺序表,以第一个元素为枢轴,使左面的元素都小于枢轴,右面的元素都大于枢轴
在这里插入图片描述
思想:

首先定义一个temp常量 ,将枢轴赋值给temp,然后定义数组两端位置i,j,i<j,左移j扫描,如果扫到比枢轴小的数字就把他移动到i位置,然后i右移,如果找到比枢轴大的元素就把他移动到j位置

实现代码:

void partition(int arr[],int n)
{
	int temp;
	int i = 0,j = n-1;
	temp = arr[i];
	while(i<j)
	{
		while(i<j&&arr[j]>=temp)
		--j;
		if(i<j)
		{
			arr[i] = arr[j]
			++i;
		}
		while(i<j&&arr[i]<temp)
		++i;
		if(i<j)
		{
			arr[j] = arr[i]
			--j;
		}
	}
	arr[i] = temp;
}

效果是把数组元素以Y为界限分成了前后两部分,前半部分小于Y,后半部分大于等于Y;
i和j最终指向的值是X,而不是Y。
习题八:
有一个线性表,采用带头结点的单链表L来存储。设计一个算法将其逆置。要求不能建立新结点,只能通过表中已有结点的重新组合来完成。
在这里插入图片描述
实现代码:

LNode *p = head->next,*q;
L->next = NULL;
while(p!=NULL)
{
	q = p->next;
	p->next = L->next;
	L->next = p;
	p = q;
}

写一个函数,逆序打印单链表中的数据,假设指针L指向了单链表的开始结点.
实现代码:

void reprint (LNode *L)
{
	if(L!=NULL)
	{
		reprint(L->next);
		cout<<L->data<<" ";
	}
}

2.10 归并

2.10.1 顺序表归并

在这里插入图片描述
实现代码:

void mergearray(int a[],int m,int b[],int n,int c[])
{
	int i,j = 0;
	int k = 0;
	while(i < m&&j<n)
	{
		if(a[i]<b[j])
			c[k++] = a[i++];
		else
			c[k++] = b[j++];
	}
	while(i<m)
		c[k++] = a[i++];
	while(j<n)
		c[k++] = b[j++];
}

2.10.2 链表归并

在这里插入图片描述
实现代码(尾插法):

void merge(LNode *A,LNode *B,LNode *&C)
{
	LNode *p = A->next;
	LNode *q = B->next;
	LNode *r;
	C = A;
	C->next = NULL;
	free(B);
	r = C;
	while(p!= NULL&&q!=NULL)
	{
		if(p->data<=q->data)
			r->next = p; p = p->next;
			r = r->next;
		else
			r->next = q; q = q->next;
			r = r->next;
	}
	if(p!=NULL)
		r->next = p;
	if(q!=NULL)
		r->next = q;
}

实现代码(头插法):逆序归并

void mergeR(LNode *A,LNode *B,LNode *&C)
{
	LNode *p = A->next;
	LNode *q = B->next;
	LNode *s;
	C = A;
	C->next = NULL;
	free(B);
	while(p!= NULL&&q!=NULL)
	{
		if(p->data<=q->data)
			s = p;p = p->next;
			s->next = C->next;
			C->next = s;
		else
			s = q;q = q->next;
			s->next = C->next;
			C->next = s;
	}
	while(p!=NULL)
		s = q;q = q->next;
		s->next = C->next;
		C->next = s;
	while(q!=NULL)
		s = q;q = q->next;
		s->next = C->next;
		C->next = s;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-07-25 11:55:27  更:2021-07-25 11:57:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 17:53:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码