DS博客作业02--线性表

0.PTA得分截图


1.本周学习总结

1.1 线性表

· 顺序表结构体


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

· 创建循序表


void CreateList(SqList& L, int n)//构建顺序表函数 ,n为顺序表长度
{
    L = new List;
    L->length = n;
    for (int i = 0; i < n; i++)
    {
        cin >> L->data[i];
    }
}

· 顺序表插入


bool Insert(List L, ElementType X, Position P)//X表示要插入的元素,P表示插入位置
{
	if (L->Last + 1 == MAXSIZE)//顺序表已满
	{
		printf("FULL");
		return false;
	}
	int i;
	if (P<0 || P>(L->Last + 1))
	{
		return false;//位置非法
	}
	for (i = L->Last; i >= P; i--)
	{
		L->Data[i + 1] = L->Data[i];
	}
	L->Data[P] = X;
	L->Last++;//最后一个位置下标++
	return true;
}

· 顺序表删除


bool Delete(List L, Position P)//P表示要插入的位置
{
	if (P<0 || P>(L->Last + 1))//非法访问
	{
		return false;
	 }
	int i;
	for (i = P; i <= L->Last-1; i++)
	{
		L->Data[i] = L->Data[i + 1];//从第i个元素开始,之后的元素往前移
	}
	L->Last--;//最后一个位置的下标--
	return true;
}

· 单链表结构体定义


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

· 两种建立链表的方式

(1)尾插法

void CreateList(LinkList& L, int n)
{
    LinkList tail, p;
    L = new LNode;
    tail = L;
    L->next=NULL;
    for (int i = 0; i < n; i++)
    {
        p = new LNode;
        cin >> p->data;
        p->next = NULL;
        tail->next = p;
        tail = p;
    }
}

图解:

(2)头插法

void CreateListF(LinkList& L, int n)
{

    LinkList pre;
    L = new LNode;
    L->next = NULL;
    ElemType i;
    for ( i = 0; i < n; i++)
    {

        pre = new LNode;
        cin >> pre->data;
        pre->next = L->next;
        L->next = pre;
    }
}

图解:

· 有序单链表插入删除

(1)插入数据

void ListInsert(LinkList& L, ElemType e)
{
	LinkList p,pre;
	p = new LNode;
	p->data = e;
	pre = L;
	while (pre->next)
	{
		if (pre->next->data > e)//找到第一个比要插入数据大的数
		{
			p->next = pre->next;
			pre->next = p;
			return;
		}
		else pre = pre->next;
	}
	p->next = pre->next;//若插入数据最大则插在最后
        pre->next = p;
}
	

(2)删除数据


void ListDelete(LinkList& L, ElemType e)
{
	if (L->next == NULL)return;
	LinkList p, pre;
	pre = L;
	while (pre->next)
	{
		if (pre->next->data == e)//遍历链表找到要删除目标位置
		{
			p = pre->next;
			pre->next = p->next;
			delete p; return;
		}
		else pre = pre->next;
	}
	cout <<e<< "找不到!"<<endl;//删除要考虑找不到的情况
}

· 合并两个单链表


void MergeList(LinkList& L1, LinkList L2)//合并链表,使合并后的链表非降排列
{
	LinkList p = L1->next, q = L2->next, L3, r, node;
	r = new LNode;
	r->next = NULL;
	L3 = r;
	while (p && q)//同时遍历
	{
		if (p->data > q->data)
		{
			node = q;
			q = q->next;//指针下移
			node->next = NULL;
			r->next = node;
			r = node;
		}
		else if (p->data < q->data)
		{
			node = p;
			p = p->next;//指针后移
			node->next = NULL;
			r->next = node;
			r = node;
		}
		else
		{
			node = p;
			q = q->next;
			p = p->next;//两个指针同时后移
			node->next = NULL;
			r->next = node;
			r = node;
		}
	}
	if (p)r->next = p;
	if (q)r->next = q;
	L1 = L3;
}

· 循环链表


对于循环链表这种设计,我们在遍历的时候的结束条件就不再是p为空的时候结束了。而是!!p等于头结点!!的时候遍历才完成。

· 双向链表


1.2学习体会

顺序表还好,链表要特别小心p和p->next是否为空,还有其他的next关系,学习链表要从基础的开始,先把链表建好,熟悉头插和尾插,在掌握其他操作如插入,删除,逆序,合并等,此外还可以了解一些C++对于链表常用的库函数,如vector和algorithm里的一些和顺序表操作有关的函数。

2.PTA实验作业

2.1 题目:6-11 链表分割

2.1.1 代码截图

2.1.2 PTA提交列表及说明


2.2题目:6-8 链表倒数第m个数

2.2.1 代码截图

2.2.2 PTA提交列表及说明


2.3题目:7-1 两个有序序列的中位数、

2.3.1 代码截图

2.3.2 PTA提交列表及说明


3.阅读代码

3.1

问题:

代码如下:
法一:



#include<iostream>
#include<algorithm>
using namespace std;
int N,W;
const int maxn=105;
int v[maxn],w[maxn];
int rec(int i,int j)//从第i个下标开始,计算剩余j重量怎么挑选商品 
{
    int ans;
    if(i==N) ans=0;//已经没有商品可以选择,递归出口。
    else if(j<w[i]) ans=rec(i+1,j);//如果背包容量装不下下标为i的商品 
    else ans=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);/*方程由来:http://blog.sina.com.cn/s/blog_e65dd0270102wd9k.html*/
    return ans;
}
int main()
{
    cin>>N>>W;
    for(int i=0;i<N;i++) cin>>w[i];
    for(int i=0;i<N;i++) cin>>v[i];
    int res=rec(0,W);
    cout<<res<<endl;
    return 0;
}


这种算法是对每个商品都进行处理,每一层搜索都有两个分支,时间复杂度为O(2^n),当n比较大的时候就会花费较多的时间。
对每个商品进行搜索的时候,有时会出现相同的参数,于是第二次调用的时候我们其实已经计算过一次了,等于是白白浪费了时间。

···

法二:


#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxN=3405;
const int maxW=405;
int dp[maxN][maxW];
int N,W;
int w[maxW],v[maxN];
int rec(int i,int j){    
     if(dp[i][j]>=0) return dp[i][j];
     int ans;
     if(i==N) ans=0;
     else if(j<w[i]) ans=rec(i+1,j);
     else ans=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);
     return dp[i][j]=ans;
 }
 int main(){
     memset(dp,-1,sizeof(dp));
     cin>>N>>W;
     for(int i=0;i<N;i++)
     {
         cin>>w[i];
         cin>>v[i];
     }
     int res=rec(0,W);
     cout<<res<<endl;
     return 0;
 }


dp[][]为记忆数组,用于记录下之前每一次的结果。我们如果把dp[i][j]定义成如下意义:当总重量小于j时,从下标为i的商品开始挑选,得到商品的最大值。于是有下面的递推公式:

法三:


#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxN=3405;
const int maxW=405;
int dp[maxN][maxW];
int N,W;
int w[maxW],v[maxN];
void  solve(){    
     for(int i=N-1;i>=0;i--){
         for(int j=0;j<=W;j++){
            if(j<w[i]) 
                 dp[i][j]=dp[i+1][j];
             else 
                 dp[i][j]=max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]);
         }
     }
         
     cout<<dp[0][W]<<endl;
}
 int main()
{
     cin>>N>>W;
     for(int i=0;i<N;i++)
     {
         cin>>w[i];
         cin>>v[i];
     }
     solve();
     return 0;
}


posted @ 2020-03-08 22:52  一个敢敢  阅读(237)  评论(0编辑  收藏  举报