DS博客作业01--线性表

0.PTA得分截图


1.本周学习总结

1.1 总结线性表内容

(1)顺序表

线性表是具有相同特性的数据元素的一个有限序列

元素个数n=表长度;
n=0(空表)
1<i<n时
ai的直接前驱是ai-1,a1无直接前驱
ai的直接后继是ai+1,an无直接后继

优点:

随机访问性强(物理地址相邻)
查找速度快

获取L中第i个元素
if 1≤i≤ListLength(L)
e=L->data[i-1]; 

缺点:

插入和删除效率低
可能浪费内存
内存空间要求高,必须有足够的连续内存空间。
数组大小固定,不能动态拓展

顺序表结构体定义

typedef struct 
{	
    int data[MaxSize];//存放顺序表元素
    int length ;//存放顺序表的长度
} List,*SqList;	

SqList L;//定义顺序表结构体变量
L->data[],L->length//引用

顺序表创建

for(i=0;i<length;i++)
{
  cin>>L->data[i];
}

顺序表插入(可拓展为顺序建表)

for (i = 0; i < L->length; i++)
{
   if (L->data[i] > x)//找到插入位置
   {
      for (j = L->length; j > i; j--)//后移
      {
         L->data[j] = L->data[j - 1];
      }
      break;
    }
}
L->data[i] = x;
L->length++;//长度增一
   

删除区间数据(法一)

j=0
for (i = 0; i < L->length; i++)
{
     //一边扫描一边计算在区间内的数据个数
     if (L->data[i] >= min && L->data[i] <= max)
     {
	j++;
     }
     else
	L->data[i-j]=L->data[i];	
}
L->length = L->length - j;//数组长度改变

删除区间数据(法二)

j=0
for (i = 0; i < L->length; i++)
{
     //不在删除区间内重构数
     if (L->data[i] < min || L->data[i] > max)
     {
	L->data[j] = L->data[i];
	j++;
     }
		
}
L->length = j;//数组长度改变

(2)链表

优点:

插入删除速度快
内存利用率高,不会浪费内存
大小没有固定,拓展很灵活。

缺点:

查找效率低

链表结构体定义

typedef struct LNode
{
   int data;
   struct LNode* next;
}LNode,*LinkList;

头插法(每次插入都在头节点后) 输入输出顺序相反.

LinkList tail;
L->next = NULL;
for (i = 0; i < n; i++)
{
   tail = (LinkList)malloc(sizeof(LNode));
   cin >> tail->data;//插入节点
   tail->next = L->next;
   L->next = tail;
}

尾插法(每次插入都在链尾)

LinkList tail;//插入节点
LinkList p;//最尾部节点
L->next = NULL;
p = L;
for (i = 0; i < n; i++)
{
   tail = (LinkList)malloc(sizeof(LNode));
   cin >> tail->data;
   p->next = tail;
   p = tail;
}
p->next=NULL;

尾部加入语句p->next=NULL方便后续链表操作使链表有结尾

数据插入

tail=new LNode;
cin>>tail->data;
tail->next=ptr->next;
ptr->next=tail;

删除操作

if(p->data=所要删除元素)
 ptr->next=p->next;
 delete p;

单链表逆置

L = L->next;
ptr = (LinkList)malloc(sizeof(LNode));//新链头节点
ptr->next = NULL;
//头插法将链L中的数据插入链ptr中
while(L!=NULL)
{
  pre = (LinkList)malloc(sizeof(LNode));
  pre->data = L->data;
  pre->next = ptr->next;
  ptr->next = pre;
  L = L->next;
}
L = ptr;//L指向ptr

(3)有序表(指有一定顺序(递增或递减)排列的线性表)

有序单链表数据插入

pre=L;
while (pre->next!=NULL && pre->next->data<e)
   pre=pre->next; //遍历查找插入位置
tail=new LinkNode;
tail->data=e;//创建存放e的数据结点tail
tail->next=pre->next;//在*pre结点之后插入*p结点
pre->next=tail;

删除

pre=L;
while (pre->next!=NULL && pre->next->data<e)
   pre=pre->next; //遍历查找插入位置
if(pre->next->data==e)
  tail=new LNode;
  tail=pre->next;
  pre->next=pre->next->next;//删除节点
  delete tail;

有序表合并(顺序表存放)

LC=new SqList; 		//建立有序顺序表LC
while (i<LA->length && j<LB->length)
{	
  //分三类将数据存放到LC
  if (LA->data[i]<LB->data[j])
  else if(LA->data[i]==LB->data[j])
     LC->data[k]=LB->data[j];
     j++;k++;i++;//相等时两表都要移动
  else	//LA->data[i]>LB->data[j]
}
while (i<LA->length)//LA尚未扫描完,将其余元素插入LC中
while (j<LB->length)//LB尚未扫描完,将其余元素插入LC中
LC->length=k;//有序表长度改变


(4)循环链表、双链表

双链表

双链表每个节点有2个指针域,一个指向后继节点,一个指向前驱节点
特点:
从任一结点出发可以快速找到其前驱结点和后继结点;
从任一结点出发可以访问其他结点

结构体定义

typedef struct DNode       //声明双链表节点类型
{	
   ElemType data;
   struct DNode *prior;    //指向前驱节点
   struct DNode *next;     //指向后继节点
} DLinkList;

插入操作

tail->next = ptr->next
ptr->next->prior = tail
tail->prior = ptr
ptr->next = tail

删除操作

ptr->next->next->prior=ptr;
ptr->next=ptr->next->next;

先改前驱,再改后继

循环链表

循环单链表

从循环链表中的任何一个结点的位置都可以找到其他所有结点,而单链表做不到
循环条件:p!=NULL(单链表)、p!=L (循环单链表)   不带头结点
         p->next!=NULL(单链表)、p->next!=L(循环单链表)   带头结点

循环双链表

链表中没有空指针域
p所指结点为尾结点的条件:p->next==L
一步操作即L->prior可以找到尾结点

(5)时间复杂度,空间复杂度

算法的效率主要是看它的时间复杂度和空间复杂度情况

时间复杂度

算法执行语句的次数
选取最高阶的项
条件模糊的一般计算最坏情况
如果算法的执行时间不随着n的增加而增长,时间复杂度是O(1)

空间复杂度

运行过程中临时占用存储空间大小
不随被处理数据量n的大小而改变时,可表示为O(1)


1.2.谈谈你对线性表的认识及学习体会。

线性表:
线性表是n个具有相同特性的数据元素的有限序列;
大部分线性表数据元素之间是一对一的关系,除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的(循环链表也算线性表);

体会:
线性表中涉及数组和链表的问题上学期就已经有了解,数组还好,但是在链表方面对于后继改变和连接的问题还是很糊,经常写着就后继全都改变,非常窒息
做完一个题集并对比之后,我对链表的连接方式和后继关系有更了解一点点

对于链表需要注意的点:
1、链表最尾端最好要加结束标志NULL,在后续对链表的操作会更方便
2、遍历链表过程中务必考虑指针是否为空,条件判断要分清楚,是ptr!=NULL,还是ptr->next!=NULL
3、没用空间要释放,能节省空间也是链表的好处
4、画链表图能更清楚地理解,更容易有思路
对于顺序表需要注意的点:
1、空间要合适不然容易溢出
2、插入,删除操作后,要注意改变顺序表长度
3、注意有些题目结构体定义的是顺序表最后一个数据的位置

2.PTA实验作业

2.1.题目1:7-2 一元多项式的乘法与加法运算

加法:
遍历两链表
若指数相同,则系数相加(若系数相加后等于零舍弃),存入新链中

乘法:(分别相乘后,多次累加)
令LA遍历一次,LB遍历多次
LB与LA的一项相乘遍历一次后形成的新链转入加法运算
多次累加后的成果就为乘法结果 

2.1.1




2.1.2本题PTA提交列表说明

错误 原因
多种错误 LA和LB相乘时,乘完一条链后没有返回链表头部
答案错误 乘法加法输出之间没有换行
部分正确 没考虑到乘法系数为零的情况
部分正确(输入有零多项式和常数多项式) 一开始理解为多项式合并后系数为零要输出“0 0”,后来才知道时多项式个数为零
部分正确(同类项合并时有抵消) 新链LC连接节点后结尾没加NULL若最后一组数据相抵消后LC链尾还是有LA中的数据

2.2 6-11 jmu-ds-链表分割

L1正序存放,L2逆序存放(头插法)
L1=L
将所需插入L2数据申请新节点存放后,头插法插入L2链

2.2.1


2.2.2本题PTA提交列表说明。

错误 原因
段错误 在L1,L2申请内存之前将其赋值给其他变量
运行超时 ptr遍历指针没向后移动,导致死循环
多种错误 在没有保存后继节点数据的情况下将ptr直接插入另一个链表,导致后继数据节点被修改
部分正确 最尾部没加NULL结束

2.3 6-8 jmu-ds-链表倒数第m个数

方法一

遍历链表并将链表逆置,求出节点个数
链表倒数第m个数就是逆置后的链表的第m个数

2.3.1


2.3.2本题PTA提交列表说明。

错误 原因
部分正确 没注意到m > len和m < 1都是错误位置

方法二

边移动边找时间复杂度更小,遍历次数更少

//求倒数第k个数
定义count=0记录移动次数
已知带头节点的链L
定义ptr,pre=L

while(ptr)
{
   ptr=ptr->next;
   count++;
}
ptr向前移动k个节点后pre开始和ptr一起向前移动
pre=pre->next;
当ptr移动到最末尾
while(ptr)
  pre所在位置就为倒数第k个数

3.阅读代码

3.1 两两交换链表中的节点


代码:

3.1.1 该题的设计思路

1、p1为需交换的节点前面一个节点
2、将节点p2与p2->next交换,这样就不需要额外记录交换节点之前的节点。
3、更新p1所在位置

3.1.2 该题的伪代码

定义
ListNode* p1=head;
ListNode* p2; 

while(p1->next!=NULL&&p1->next->next!=NULL)
{
     //将需要交换节点之间next关系逆转
     p1移动到需交换的节点前面一个节点
     将节点p2与p2->next交换//这样不需要额外记录交换节点之前的节点
     p1=p2; //更新p1位置
}

3.1.3 运行结果

3.1.4分析该题目解题优势及难点

优势:
清晰明了,可读性强,
用next关系逆转解决节点交换,在链表上直接实现,不用重新开一条链,节省空间
难点:
用next关系逆转解决节点交换,会使节点后继关系容易混乱,所以移动到的下一个位置的判断和连接关系很重要
更新下一个反转位置时要先储存好后继节点


3.2 K 个一组翻转链表


代码:

3.2.1 该题的设计思路

3.2.2 该题的伪代码

while(last)
  遍历链表,求链表长度count
求需要循环的次数count /= k;
while(k!=1 && count--) 
{
   q = head->next;
   p = head;
   //实现链表翻转
   while(n>2) 
  { 
     r = q->next;//保留后继节点
     改变需要逆转节点之间的next关系
     最后使需要逆转的最后一个位置是q,q前面是p
     n--;
   }
    q指向需要反转的最后一个节点,q->next = p实现最后一步的链表反转
    的后继节点head->next = q->next;
    pre->next = q;
    pre = head;更新前驱节点
    更新头结点head = head->next;
}

3.2.3 运行结果

3.2.4分析该题目解题优势及难点

优点:
链表关系运用灵活,可读性强
在链表上直接实现,不用重新开一条链,节省空间
难点:
不带头节点的链表反转时就需要重新申请头节点,反转后头节点也需跟着变动
需要及时更新头节点的位置
最初位置的反转关系不能错乱,更新下一个反转位置时要先储存好后继节点

posted @ 2020-03-08 22:32  泗汐  阅读(246)  评论(0编辑  收藏  举报