DS博客作业02--线性表

0.PTA得分截图


1.本周学习总结

1.1 总结线性表内容

  • 顺序表:把线性表中的所有元素按照顺序存储方法进行存储的结构为顺序表,即物理地址相邻(注:顺序表中的元素排列不一定为有序)
    • 顺序表的结构体定义:

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

  • 顺序表的插入:

    void InsertSq(SqList& L, int x)
    {
        int i, j;
        SqList ptr;
        ptr = L;
        i = 0;
    
        while (ptr->data[i] < x)
        {
            i++;
            if (i == ptr->length) break;
        }
        if (i == ptr->length) 
        {
            ptr->data[i] = x;
            ptr->length += 1;
        }
        else
        {
            for (j = ptr->length; j > i; j--)
            {
       	     ptr->data[j] = ptr->data[j - 1];
            }
            ptr->data[i] = x;
            ptr->length += 1;
        }
    }
    
  • 顺序表的删除:

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

  • 链表:链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
    • 链表结构体定义:

typedef struct LNode //定义单链表结点类型
{
ElemType data;
struct LNode next; //指向后继结点
} LNode,
LinkList;
```

  • 链表的头插法:所有的新增节点都插在头结点L的后面
    void CreateListF(LinkList& L, int n)//头插法建链表,L表示带头结点链表,n表示数据元素个数
    {
        L = new struct LNode;
        L->next = NULL;
    	     LinkList ptr =L;
    	     int i = 0;
        int num;
    
        while (i < n)
        {
            cin >> num;
            LinkList p = new struct LNode;
            p->data = num;
            p->next = ptr->next;
            ptr->next = p;
            i++;		
        }
    }
    
  • 链表的尾插法:新增节点从链表尾部插入
    void CreateListR(LinkList& L, int n)
    {
        LinkList pre;
        LinkList ptr;
        int i = 0;
    
        L = new LNode;
        L->next = NULL;
        pre = L;
    
        while (i < n)
        {
       ptr = new LNode;
       cin >> ptr->data;
       ptr->next = NULL;
       pre->next = ptr;
               pre=pre->next;
       i++;
        }
    }
    
  • 链表插入、删除:
    void ListInsert(LinkNode*& L, ElemType e)
    {
    
        LinkNode* pre = L, * p;
    
        while (pre->next != NULL && pre->next->data < e)
    
            pre = pre->next;
    
        //查找插入人结点的前驱结点pre
    
        p = (LinkNode*)malloc(sizeof(LinkNode));
    
        p - > data = e;
    
        //创建存放e的数据结点p
    
        p - > next = pre - > next;
    
        //在pre结点之后插人p结点
    
        pre - > next = p;
    }
    
    Status ListDelete_L(LinkList& L, int i, ElemType& e) //在带头结点的单链表L中,删除第i个元素,并由e返回其值
    {
        LinkList p;
        LinkList q;
        p = L; int j = 0;
        while (p->next && j < i - 1) //寻找第i个节点,并令p指向其前驱
        {
            p = p->next; ++j;
        }
        if (!(p->next) || j > i - 1) return ERROR; //删除位置不合理
        q = p->next;
        p->next = q->next;
        e = q->data;
        free(q);
        return OK;
    }
    
    
  • 有序表:
    • 有序单链表数据插入、删除
      void ListInsert(LinkList& L, ElemType e)//有序链表插入元素e
      {
          LinkList pre, ptr;
      
          ptr = new LNode;
          ptr->data = e;
          ptr->next = NULL;
          pre = L;
          while (1)
          {
              if (pre->next == NULL)
              {
              	     pre->next = ptr;
         	     break;
              }
              if ( pre->next->data > e)
              {
         	     ptr->next = pre->next;
         	     pre->next = ptr;
         	     break;
              }
              pre = pre->next;
          }
      }
      void ListDelete(LinkList& L, ElemType e)//链表删除元素e
      {
          LinkList pre, ptr;
      
          pre = L;
          ptr = NULL;
          if (pre->next == NULL) return;
          while (pre->next != NULL)
          {
              if (pre->next->data == e)
              {
         	     ptr = pre->next;
         	     break;
              }
              pre = pre->next;
          }
          if (ptr == NULL)
          {
              printf("%d找不到!
      ",e);
          }
          else
          {
              pre->next = ptr->next;
              delete ptr;
          }
      }
      
    • 有序表合并
      void MergeList(LinkList& L1, LinkList L2)//合并链表
      {
          LinkList ptr, ptr1, ptr2;
      
          ptr1 = L1->next;
          ptr2 = L2->next;
          ptr = L1;
      
          while (ptr1 && ptr2)
          {
              if (ptr1->data > ptr2->data)
              {
         	     ptr->next = ptr2;
         	     ptr = ptr2;
         	     ptr2 = ptr2->next;
              }
              else if(ptr1->data < ptr2->data)
              {
         	     ptr->next = ptr1;
         	     ptr = ptr1;
         	     ptr1 = ptr1->next;
              }
              else
              {
         	     ptr->next = ptr1;
         	     ptr = ptr1;
         	     ptr1 = ptr1->next;
         	     ptr2 = ptr2->next;
              }
          }
          if (ptr1 == NULL) ptr->next = ptr2;
          else ptr->next = ptr1;
      }
      

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

  • 线性表是具有相同数据类型的n个数据元素的有限序列,其逻辑关系为“一对一”。线性表的两种存储结构为上述提到的顺序表和链表,这两种方法各有优势。顺序表最大的优点就是可随机存取任一元素,无需遍历,但顺序表的插入、删除操作都需要移动大量的元素,且预先分配空间时需按最大空间分配,对空间的利用不充分。而链表的插入、删除操作都较为方便,空间的分配也比顺序表更为灵活。在使用链表的过程中一定要注意指针是否为空,否则会非法访问内存,导致程序出错。
  • 学习体会:本章主要是在讲链表,感觉自己在链表这块还是较为薄弱,指针的取名也还需要更加注意,要做到见名知意。

2.PTA实验作业

2.1 链表倒数第m个数

2.1.1 代码截图

2.1.2 PTA提交列表及说明


1.部分正确:我最初的做法是将链表倒置,然后找正数第m个数,然而在链表倒置的程序未能很好地完成功能。
2.部分正确:修改后,依旧未能完成链表的倒置。
3.答案正确:在听完老师的讲解后,发现可以正数地找第len-m个数,修改代码后,答案正确。

2.2 有序链表的插入删除

2.2.1 代码截图

2.2.2 PTA提交列表及说明

1.部分正确:刚开始写时,我的删除函数是会去找要删除掉的每一个数据并作出判断,假设我们要删除五个数,即使前四个数删除完后链表已经为空,依旧会去链表中找第五个数,并作出判断,输出“x找不到!”,所以无法通过链表全删的测试点,于是我在函数里加了一个判断链表是否为空的语句。
2.部分正确:在函数中加入链表是否为空的语句时,我把该语句写错了位置,放到了比较后面,于是未能通过。
3.答案正确:我把判断链表为空的语句放在了函数中较前的位置,于是通过了。

2.3 有序链表合并

2.3.1 代码截图

2.3.2 PTA提交列表及说明

1.部分正确:一开始写的时候,并未考虑到两个链表中有重复数据的情况,只用了两个分支进行判断,于是没有通过重复数据的测试点。
2.答案正确:多加了一个判断两个链表中的数据是否相等的分支后,答案正确。

3.阅读代码

3.1 题目及解题代码


解题代码:

#include <iostream>
using namespace std;
typedef struct LNode
{
	string instruction;
	int length;
	struct LNode* next;
} LNode, * LinkList;
bool isRobotBounded(LinkList L);
int main()
{
	LinkList L;
	L = new LNode;

	cin >> L->instruction;
	L->length = L->instruction.size();
	if (isRobotBounded(L))
	{
		cout << "true";
	}
	else
	{
		cout << "false";
	}
	delete L;
	return 0;
}
bool isRobotBounded(LinkList L)
{
	int flag = 0; //flag=0方向朝上,flag=1方向朝做,flag=2方向朝下,flag=3方向朝右
	int x = 0, y = 0;
	for (int i = 0; i < L->length; i++) 
	{
		if (L->instruction[i] == 'G')
		{
			if (flag == 0) y++;
			if (flag == 1) x--;
			if (flag == 2) y--;
			if (flag == 3) x++;
		}
		else if (L->instruction[i] == 'L') 
		{
			flag += 1;
			flag = flag % 4;
		}
		else if (L->instruction[i] == 'R')
		{
			flag += 3;
			flag = flag % 4;
		}
	}
	/*一轮循环后回到原点*/
	if (x == 0 && y == 0)  return true;
	/*机器人最后的朝向如果是朝左朝右或朝下就一定能在后续循环中回到原点*/
	if (flag % 4 == 0)   return false;
	return true;
}

3.1.1 该题的设计思路

在无限的平面上,机器人从原点(坐标为(0,0))处按照指令出发,G为朝当前机器人所朝方向前进一格,L和R只让机器人转向不前进。机器人不断重复指令,判断机器人所走路径是否为一个环。

3.1.2 该题的伪代码

输入指令,存在字符串instruction中
for i = 0 to i = L->length do
    if 指令为G then
		若flag == 0 y++;
        若flag == 1 x--;
        若flag == 2 y--;
        若flag == 3 x++;
	end if
	if 指令为L then
		flag += 1;
	    flag = flag % 4;/*防止flag越界*/
	end if
	if 指令为R then
		flag += 3;
	    flag = flag % 4;
	end if
end for
if 机器人此时在原点 then
   return true;
end if
if 机器人此时朝向上 then
   return false;
else
   return true;
end if

3.1.3 运行结果



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

  • 该题难点在于机器人会在过程中不断地转向,而转向后再前进时坐标该如何改变比较难确定。且如果机器人是在一个环中行走的,但我们无法确定机器人是在第几轮循环的末尾回到原点。
  • 该代码的解题优势在于flag变量,该代码利用flag的值的改变来代表机器人不同的朝向(flag=0方向朝上,flag=1方向朝做,flag=2方向朝下,flag=3方向朝右)。且这题的解题思路是判断一轮循环之后机器人的朝向,以此确定机器人是否在一个环中,避免代码的时间复杂度过大。
原文地址:https://www.cnblogs.com/w60-06/p/12436408.html