DS博客作业02--线性表

0.PTA得分截图

1.本周学习总结(0-4分)

1.1 总结线性表内容

1.线性表

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

  • 线性表的抽象数据类型描述:ADT结构

2.顺序表

  • 初始化InitList(结构体定义)
typedef int ElemType; 
typedef struct 
{      
     ElemType data[MaxSize];  //存放顺序表元素
      int length ;		    //存放顺序表的长度
} List;	
typedef List *SqList;
void CreateList(SqList &L,int n){
   int i;
   L=new List;
   L->length=n;
   for(i=0;i<n;i++)  cin>>L->data[i];
}

时间复杂度:O(1)

  • 插入ListInsert
bool ListInsert(List &L,int i,ElemType e)
{  int j;
   if (i<1 || i>L->length+1)
	return false;	//参数错误时返回false
   i--;	//将顺序表逻辑序号转化为物理序号
for (j=L->length;j>i;j--)	//将data[i..n]元素后移一个位置
  L->data[j]=L->data[j-1];
L->data[i]=e;			//插入元素e
L->length++;			//顺序表长度增1
return true;			//成功插入返回true
}

时间复杂度:O(n)

*删除ListDelete

bool ListDelete(List &L,int i,ElemType &e)
{  
   if (i<1 || i>L->length) //删除位置不合法
        return false;
   i--;		    //将顺序表逻辑序号转化为物理序号
   e=L->data[i];
   for (int j=i;j<L->length-1;j++)         
      L->data[j]=L->data[j+1];
   L->length--;	    //顺序表长度减1
   return true;			
}

时间复杂度:O(n)

  • 其他函数:销毁DestroyList(free函数/delete函数)、判定空表ListEmpty(bool类型的应用)、

长度读取ListLength、输出数据DispList、获取指定元素GetElem、按值查找元素LocateElem等

3.链表

  • 初始化(结构体定义)
typedef struct LNode{
     ElemType   data;       //数据域
     struct LNode  *next;   //指针域
}LNode,*LinkList; 
Status InitList_L(LinkList &L){ 
   L=new LNode;                    	
   L->next=NULL;     
   return OK; 
}

时间复杂度:O(1)

  • 建立链表

    • 头插法(新增节点从链表头部插入)

图像表示

代码实现

void CreateListF(LinkList &L,ElemType a[],int n){
	int i;
           L=new LNode;
	L->next=NULL; 	
          LinkList nodePtr;
	for(i=0;i<n;i++){
		 nodePtr=new LNode;
		 nodePtr->data=a[i];
		 nodePtr->next=L->next;
		L->next= nodePtr;
	}
}
  • 尾插法(新增节点从链表尾部插入)

图像表示

代码实现

void CreateListR(LinkList &L,ElemType a[],int n){
	int i;
	LinkList  nodePtr,tailPtr;
            L=new LNode;
	L->next=NULL; 	
	tailPtr=L;//尾指针
	for(i=0;i<n;i++)  {
	  	nodePtr=new LNode;
		nodePtr->data=a[i];
		rearPtr->next=s;//尾部插入新结点
		rearPtr=s;  }
       nodePtr->next=NULL;
}
  • 插入节点ListInsert

图像表示

代码实现

bool ListInsert(LinkList &L,int i,ElemType e){
  int j=0;
  LinkList p=L,s;
  while(p&&j<i-1){
  	j++;p=p->next;
  }
  if(p==NULL) return false; //未找到第i-1个结点
  s=new LNode;
  s->data=e;
  s->next=p->next;  //插入p后面
  p->next=s;	
  return true;
}
  • 删除节点ListDelete

图像表示

代码实现

bool ListDelete_L(LinkList &L,int i,ElemType &e)
{
	 int j=0;
  LinkList p=L,s,q;
  while(p&&j<i-1){  
  	p=p->next;j++;
  }
  if(p==NULL) return false;
	q=p->next;  //第i个位置
  if(q==NULL) return false;	
      e=q->data;
      p->next=q->next;//改变指针关系,删除
      delete q;
     return true;
}
  • 其他函数:销毁链表DestroyList、判断空表ListEmpty、读取长度ListLength、

输出数据DispList、查找节点GetElem等

4.有序表

  • 定义:所有元素以递增或递减方式有序排列

  • 插入数据ListInsert

有序顺序表的代码实现

void ListInsert(SqList &L,ElemType e)
{     int i=0,j;
      while (i<L->length && L->data[i]<e)
	i++;			//查找值为e的元素
      for (j=ListLength(L);j>i;j--)	//将data[i..n]后移一个位置
	L->data[j]=L->data[j-1]; 
      L->data[i]=e;
      L->length++;		//有序顺序表长度增1
}

有序单链表的代码实现

void ListInsert(LinkNode &L,ElemType e)
{     LinkNode pre=L,p;
      while (pre->next!=NULL && pre->next->data<e)
	pre=pre->next; 	//查找插入结点的前驱结点*pre
      p=new LinkNode;
      p->data=e;		//创建存放e的数据结点*p
      p->next=pre->next;	//在*pre结点之后插入*p结点
      pre->next=p;
}
  • 删除数据ListDelete

有序顺序表的代码实现

int ListDelete(SqList &L,int i)
{
	if(i<=0||i>L.length) return 0;
	for(int j=i-1;j<L.length;i++)
	{
		L.elem[j]=L.elem[j+1];
	}
	L.length--;
	return 1;
}

有序单链表的代码实现

void ListDelete(LinkList &L, ElemType e) {
     LinkList p = new(LNode);
    p = L;
    if (p->next == nullptr)
         return;
    while (1) { 
         if (p != nullptr&&p->next != nullptr) {
             if (e == p->next->data) {
                 p->next = p->next->next;
                 return;
                 }
         }
         if (p == nullptr)
             break;
         p = p->next;
     }
    cout << e << "找不到!" << endl;
 }
  • 二路归并UnionList

图像表示

代码实现

void UnionList(SqList  LA,SqList  LB,SqList  &LC)
{     int i=0,j=0,k=0;//i、j分别为LA、LB的下标,k为LC中元素个数
      LC=new SqList; 		//建立有序顺序表LC
      while (i<LA->length && j<LB->length)
      {	if (LA->data[i]<LB->data[j])
	{     LC->data[k]=LA->data[i];
	       i++;k++;
	}
	else	//LA->data[i]>LB->data[j]
	{     LC->data[k]=LB->data[j];
	      j++;k++;
	}
       }   
while (i<LA->length)		//LA尚未扫描完,将其余元素插入LC中
     {	LC->data[k]=LA->data[i];
	i++;k++;
     }
     while (j<LB->length)		//LB尚未扫描完,将其余元素插入LC中
     {	LC->data[k]=LB->data[j];
	j++;k++;
     }
     LC->length=k;
}

5.双链表

双链表每个节点有2个指针域,一个指向后继节点,一个指向前驱节点。类型定义如下:

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

双链表有点:
• 从任一结点出发可以快速找到其前驱结点和后继结点;
• 从任一结点出发可以访问其他结点。

6.循环链表

循环链表是另一种形式的链式存储结构形式。

循环单链表:将表中尾结点的指针域改为指向表头结点,整个链表形成一个环。
由此从表中任一结点出发均可找到链表中其他结点。 

循环双链表与非循环双链表的比较

与非循环双链表相比,循环双链表:

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

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

本学期在C++语法的基础上进行对线性表知识的代码编写,得益于C++的精短性,在代码可读性上得到了不错的提升。

在线性表的基础上拓展学习了双链表和循环链表的相关知识,使得我对链表的掌握与操纵更加熟练。

在经过本章的学习后,更加懂得了好算法的重要性,好的算法不仅能够使代码更加精炼,还能减小程序的时间与空间复杂度。

在接下来的学习中,能够对链的应用更加熟练,为后续编写程序打下良好的基础。

2.PTA实验作业(0-2分)

2.1.题目1:6-2 jmu-ds-有序表插入数据 (25分)

2.1.1代码截图


2.1.2本题PTA提交列表说明。

1.部分正确:在移动数组元素时没有考虑到数组越界的问题

2.部分正确:在插入完成后没有对数组的长度进行增加

3.答案正确

2.2 6-3 jmu-ds- 顺序表删除重复元素 (25分)

2.2.1代码截图


2.2.2本题PTA提交列表说明。

1.部分正确:在删除重复元素前移后面元素的循环时循环始条件使用错误

2.部分正确:没有考虑到当最后仅剩两个元素时依旧重复的问题

3.答案正确

2.3 6-8 jmu-ds-链表倒数第m个数 (20分)

2.3.1代码截图

2.3.2本题PTA提交列表说明。

1.运行时错误:开头定义的代运行p指针指向错误

2.部分正确:没有考虑无效位置情况下对应的返回内容

3.答案正确

3.阅读代码(0--4分)

3.1 题目及解题代码

题目:

代码:

ListNode* swapPairs(ListNode* head) {
        ListNode *dummyHead = new ListNode(-1);
        dummyHead->next = head;
        ListNode *ptr = dummyHead;
        while(ptr->next != nullptr && ptr->next->next != nullptr)
        {
            ListNode *firstNode = ptr->next;
            ListNode *secondNode = ptr->next->next;
            //交换节点
            ptr->next = secondNode;
            firstNode->next = secondNode->next;
            secondNode->next = firstNode;
            ptr = firstNode;
        }
        ListNode *retNode = dummyHead->next;
        delete dummyHead;
        return retNode;
    }

3.1.1 该题的设计思路

时间复杂度:O(n)

空间复杂度:O(1)

3.1.2 该题的伪代码

ListNode* swapPairs(ListNode* head) 
{
    声明哑结点dummyHead和指针ptr
    分别并指向头节点head和头节点head的前驱
while(链表不为空)
        {
            firstNode遍历链表中的偶数节点
            //交换节点
            secondNode遍历链表中的奇数节点,并交换节点
        }
end while
    ListNode *retNode = dummyHead->next;
    重置节点并释放空间
    返回节点
}

3.1.3 运行结果

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

优势:

本题的递归和非递归解法其实原理类似,都是更新每两个点的链表形态完成整个链表的调整

其中递归解法还可以作为典型的递归解决思路进行讲解

难点:
递归写法要观察本级递归的解决过程,形成抽象模型,因为递归本质就是不断重复相同的事情。

而不是去思考完整的调用栈,一级又一级,无从下手。

3.2 题目及解题代码

题目:

代码:

ListNode* oddEvenList(ListNode* head) {
        if(!head || !head->next || !head->next->next)
            return head;
        //声明虚拟头结点
        ListNode dummyOdd(0);
        //dummyOdd.next = head;
        auto p = &dummyOdd;
        ListNode dummyEven(0);
        //dummyEven.next = head->next;
        auto q = &dummyEven;
        int flag = 0;
        while(head){
            flag++;
            if(flag % 2 != 0){
                p->next = head;
                p = p->next;
            }
            else{
                q->next = head;
                q = q->next;
            }
            head = head->next;
        }
        q->next = NULL;
        p->next = dummyEven.next;
        return dummyOdd.next;
    }

3.2.1 该题的设计思路


时间复杂度: O(n)

空间复杂度: O(1)

3.2.2 该题的伪代码

ListNode* oddEvenList(ListNode* head){
    if(head为NULL或一个节点或两个节点)
        return head;
    声明两个虚拟头结点:dummyOdd(0),dummyEven(0);
    while(循环遍历每个头结点){
        if(节点序号为奇数)
            插入到dummyOdd链表中(尾插法,只改变指针指向,不申请其他节点)
        else(节点序号为偶数)
            插入到dummyEven链表中(尾插法,只改变指针指向,不申请其他节点)

        将dummyEven链表尾节点置空,防止出现循环链表(如1->2->3->4->5->NULL);
        将dummyOdd尾节点指向dummyEven头结点;

        return dummyOdd头结点的下一个节点;
    }
}

3.2.3 运行结果

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

优势:

将奇节点放在一个链表里,偶链表放在另一个链表里,然后把偶链表接在奇链表的尾部

这个解法非常符合直觉思路也很简单

难点:

遍历整个链表至少需要一个指针作为迭代器

这里 odd 指针和 even 指针不仅仅是尾指针,也可以扮演原链表迭代器的角色

即使思路直观,但要写一个精确且没有bug的代码还是需要进行一番思索的

posted @ 2020-03-06 15:31  BaiYi#  阅读(528)  评论(0编辑  收藏  举报