引言
最大子序列和
整数序列A1, A2,… An (可能有负数),求A1~An的一个子序列Ai~Aj,使得Ai到Aj的和最大
注意:序列是有顺序的,不是把所有正数相加就可以的
#include <iostream>
using namespace std;
int Data[]={-2, 11, -4, 13, -5, 2, -5, -3, 12, -9};
int MaxSubSum(int *pData,int nLen)
{
int nTmp=0;
int nSum=0;
for(int i=0;i<nLen;i++)//序列头a[i]
for(int j=i;j<nLen;j++)//序列尾a[j]
{
nSum=0;
for(int k=i;k<j;k++)
nSum+=pData[k];//a[i]~a[j]的和
if(nSum>nTmp)
nTmp=nSum;
}
return nTmp;
}
int main(int argc, char *argv[])
{
cout << MaxSubSum(Data,sizeof(Data)/sizeof(int)) << endl;
return 0;
}
//运行时间为O(N^3)
线性结构
线性表
线性表是一个含有n≥0个结点的有限序列,对于其中的结点,有且仅有一个开始结点没有前驱但有一个后继结点,有且仅有一个终端结点没有后继但有一个前驱结点,其它的结点都有且仅有一个前驱和一个后继结点。
线性表基本操作
初始化:malloc
- 销毁:free
- 置空:len
- 判断是否空:len
- 获取长度:len
- 根据位置查找元素
- 根据元素查找位置
- 获取指定元素的前驱和后继
- 插入元素
- 删除元素
- 遍历
线性表实现
顺序线性表
特点
- 顺序线性表的物理存储地址连续
- 随机读取效率高
实现
#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
#define INIT_SIZE 10
#define INCREAMENT_SIZE 5
typedef int Sta;
typedef int EType;
typedef struct
{
EType *e;
int len;//当前长度
int size;//分配的表长度
}SqList;
Sta InitList(SqList *L)
{
L->e=(EType*)malloc(INIT_SIZE*sizeof(EType));
if(!L->e)
return ERROR;
L->len=0;
L->size=INIT_SIZE;
return OK;
}
//销毁操作,把表的整个结构给销毁掉,把原来所占有的内存空间都给释放出来
Sta DestroyList(SqList *L)
{
if(L->e)
free(L->e);
L->len=0;
L->size=0;
return OK;
}
//清空操作,把表中的元素清空,但表结构依然存在于内存中
Sta ClearList(SqList *L)
{
L->len=0;
return OK;
}
Sta IsEmpty(const SqList* L)
{
return (bool)(L->len);
}
Sta GetLen(const SqList *L)
{
return L->len;
}
Sta GetElem(const SqList* L,int i,EType *e)
{
if(i<1 || i>L->len)
return ERROR;
*e=L->e[i-1];
return OK;
}
//返回位置1为起点,非偏移量
Sta FindElem(const SqList *L,EType e,int *pos)
{
int i=0;
*pos = 0;
for(i=0;i < L->len;i++)
{
if(*(L->e) == e)
*pos = i+1;
}
if(0 == *pos)
return ERROR;
return OK;
}
//插入位置1为起点,非偏移量
Sta InsertElem(SqList *L,int i,EType e)
{
EType *elem;
if(i<1 || i>L->len+1)
return ERROR;
//判断容量 容量不够先扩容
if(L->len >= L->size)
{
elem = (EType*)realloc(L->e,(L->size+INCREAMENT_SIZE)*sizeof(EType));
if(!elem)
return ERROR;
L->e = elem;
L->size += INCREAMENT_SIZE;
}
//扩容后再插入数据
EType *p = &L->e[i-1];//要插入位置的前一个元素的地址
EType *q = &L->e[L->len-1];//列表最后一个元素的地址
//整体向右移动1位
for(;q>=p;q--)
*(q+1)=*q;
*p=e;
++L->len;
return OK;
}
Sta DeleteElem(SqList* L,int i,EType *e)
{
if(i<1 || i>L->len)
return ERROR;
EType *p = &L->e[i-1];
*e = *p;
//整体向左移动1位
for(;p<&L->e[L->len-1];p++)
*p = *(p+1);
--L->len;
return OK;
}
void Visit(EType e)
{
printf("%d ",e);
}
Sta TraverseList(const SqList L,void(*visit)(EType))
{
int i;
for(i=0;i<L.len;i++)
visit(L.e[i]);
return OK;
}
int main()
{
SqList L;
if(InitList(&L))
{
for(int i=0;i<10;i++)
InsertElem(&L,i,i);
TraverseList(L,Visit);
int elem;
DeleteElem(&L,9,&elem);
Visit(elem);
TraverseList(L,Visit);
GetElem(&L,8,&elem);
Visit(elem);
TraverseList(L,Visit);
}
return 0;
}
//注意:size和length的区别
链式线性表
特点
- 链式线性表的物理存储地址不连续
- 插入删除效率高
实现
- 单链表:头结点无前一个节点 尾结点无后一个结点
- 循环链表:尾结点指向头结点
- 双向链表:相邻结点间双向指向,头结点与尾结点双向指向
//单链表
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#define OK 1
#define ERROR 0
typedef int EType;
typedef int Sta;
typedef struct Node
{
EType data;
struct Node *next;
}*LinkList;
//把各个函数的入口设为head名比L名更好,因为传入链表的时候传入的是头指针
//初始化只创建头结点 头结点next指针指向为NULL
//这里传入的必须是头结点的指针 否则这和函数传入一个临时变量一样!
Sta InitList(LinkList* head)
{
*head = (LinkList)malloc(sizeof(Node));
if(!head)
return ERROR;
(*head)->next = NULL;
return OK;
}
//销毁是把链表这个结构的内存都释放了
void DestroyList(LinkList *head)
{
LinkList p = (*head)->next;
LinkList q;
while(p)//终止条件为结点为NULL
{
q = p;
p = p->next;
free(q);//释放当前结点内存时,先临时保存当前结点指向下一结点的指针
}
free(*head);//释放头结点
}
//清空是把链表中的元素清空,但链表还存在
//头结点指向为NULL 销毁头结点以后的结点
void ClearList(LinkList head)
{
LinkList p = head->next;
head->next = NULL;
DestroyList(&p);
}
bool IsEmpty(LinkList head)
{
if(head->next)
return false;
else
return true;
}
int GetLength(LinkList head)
{
int i=0;
LinkList p = head->next;
while(p)
{
i++;
p = p->next;
}
return i;
}
Sta GetElem(LinkList head,int i,EType *e)
{
int j = 1;
LinkList p = head->next;
while(p && j<i)
{
j++;
p = p->next;
}
if(!p || j>i)
return ERROR;
*e = p->data;
return OK;
}
Sta FindElem(LinkList head,EType e,int *pos)
{
int i = 0;
LinkList p = head->next;
while(p)
{
i++;
if(e == p->data)
{
*pos = i;
return OK;
}
p = p->next;
}
return ERROR;
}
//malloc一个结点,找到要插入的结点位置,然后插入该结点
Sta InsertElem(LinkList head,int i,EType e)
{
int j = 0;
LinkList p = head;
LinkList s = (LinkList)malloc(sizeof(Node));;
while(p && j<i-1)
{
j++;
p = p->next;
}
if(!p || j>i-1)
return ERROR;
s->next = p->next;
p->next = s;
s->data = e;
return OK;
}
//找到要删除位置的结点,然后删除该结点,free掉内存
Sta DeleteElem(LinkList head,int i,EType *e)
{
int j=0;
LinkList p = head;
LinkList s;
while(p && j<i-1)
{
j++;
p = p->next;
}
if(!p->next || j>i-1)
return ERROR;
s = p->next;
p->next = s->next;
*e = s->data;
free(s);
return OK;
}
//头插法:在头结点head之后插入数据
void ReverseList1(LinkList head)
{
LinkList p = head->next;
LinkList q;
while(p->next)//第一轮将:第二个结点拉到头结点与第一个结点之间
{
q = p->next;
p->next = q->next;
q->next = head->next;
head->next = q;
}
}
//递归实现逆序打印(不改变链表结构)
void ReversePrint(LinkList head)
{
if(head->next)
ReversePrint(head->next);
printf("%d ",head->data);
}
void Visit(EType e)
{
printf("%d ",e);
}
void TraverseList(LinkList head,void (*visit)(EType))
{
LinkList p = head->next;
while(p)
{
visit(p->data);
p = p->next;
}
}
int main()
{
LinkList L;
InitList(&L);
for(int i=1;i<10;i++)
InsertElem(L,i,i);
TraverseList(L,Visit);
printf("\n*****1******\n");
ReversePrint(L->next);
printf("\n*****2******\n");
ReverseList1(L);
TraverseList(L,Visit);
printf("\n*****3******\n");
int nLen = GetLength(L);
printf("\n*****%d******\n",nLen);
int e;
GetElem(L,1,&e);
printf("\n*****%d******\n",e);
int pos;
FindElem(L,8,&pos);
printf("\n*****%d******\n",pos);
ClearList(L);
nLen = GetLength(L);
printf("\n*****%d******\n",nLen);
DestroyList(&L);
printf("\n*****OV******\n",nLen);
return 0;
}
代码总结
在创建链表和销毁链表的函数中,函数的入参应是链表的头结点(指针)地址
&L
在进行链表的其他操作时,函数入参不必是头结点(指针)地址,直接传入头结点即可,因为结点之间的关系使用指针表示的
遍历的时候从第一个结点(非头结点)开始较好些
逆序头插法示意图
链表应用
- 实现一元多项式的加法
//数据结构
typedef struct Node
{
float coef;//系数
int expn;//指数
struct Node *next;
}*LinkList;
//思路:将两个链表相同指数的系数相加