zoukankan      html  css  js  c++  java
  • 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的代码还是需要进行一番思索的

  • 相关阅读:
    理解.NET中的异常(二)
    路径,文件,目录,I/O常见操作汇总(一)
    使用PInvoke.NET插件为托管代码添加Win32 API签名
    使用JavaScript检测浏览器的相关特性
    javascript中的变量
    曾经的你
    WinampMy Favorite Media Player
    新的开始,认真地写博客
    log4net的各种Appender配置示例
    使用GhostDoc为代码生成注释文档
  • 原文地址:https://www.cnblogs.com/yushanbaiyi/p/12389865.html
Copyright © 2011-2022 走看看