给定一个顺序存储的线性表,请设计一个算法查找该线性表中最长的连续递增子序列。例如,(1,9,2,5,7,3,4,6,8,0)中最长的递增子序列为(3,4,6,8)。
输入格式:
输入第1行给出正整数n(≤100000);第2行给出n个整数,其间以空格分隔。
输出格式:
在一行中输出第一次出现的最长连续递增子序列,数字之间用空格分隔,序列结尾不能有多余空格。
输入样例:
15
1 9 2 5 7 3 4 6 8 0 11 15 17 17 10
输出样例:
3 4 6 8
代码:
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 1000001
///MAXSIZE大于1000000(题目要求的数值)是为了避免特殊情况溢出
typedef int ElementType;
struct LNode
{
ElementType Data[MAXSIZE];
int Length;
};
typedef struct LNode* List;
//初始化顺序表
List initSeqList()
{
List L;
L = (List)malloc(sizeof(struct LNode));
L->Length = 0;
return L;
}
//传值
void getValue(List L, int n)
{
List p = L;
int i;
for (i = 0; i < n; i++)
scanf("%d", &(p->Data[i]));
p->Length = n;
}
//寻找最长连续递增子序列
void getMaxLength(List L, int n)
{
List p = L;
int i, j = 0, lengths = 1; //lengths为1, 表示长度只有头部
int lengthHead = 0; //lengthHead为该连续子列与下一个比较的前者(简称:比较头)
int lengthTail = 1; //lengthTail为该连续子列比较的后者(简称:比较尾)
int oldHead = 0; //每段连续子列的原头部下标
int maxLength; //最大长度的连续子列
int position; //最大长度的头点下标
for (i = 0; i < n; i++)
{
if (p->Data[lengthHead] < p->Data[lengthTail]) //满足连续子列的条件
{
lengthHead++; //比较头 后移一位
lengthTail++; //比较尾 后移一位
lengths++; //该段长度+1
}
else if (p->Data[lengthHead] >= p->Data[lengthTail]) //不满足连续子列的条件
{
if (j == 0) //第一次传值
{
maxLength = lengths; //将该连续子列的长度赋值给length
position = oldHead; //将该连续子列的原头下标赋值给head
j++;
}
if (j == 1) //第二次及以上传值
{
if (maxLength < lengths)
{
maxLength = lengths;
position = oldHead;
}
}
lengthHead = lengthTail; //比较头变比较尾
oldHead = lengthTail; //原头变比较尾
lengthTail++; //比较尾向后移一位
lengths = 1; //长度重新赋值为1
}
}
for (i = 0; i < maxLength; i++) //输出最长子列
{
if (i == 0)
printf("%d", p->Data[position]);
else
printf(" %d", p->Data[position]);
position++;
}
}
int main()
{
List L;
int n;
L = initSeqList();
scanf("%d", &n);
getValue(L, n);
getMaxLength(L, n);
return 0;
}
分析:
#define MAXSIZE 1000001
///MAXSIZE大于1000000(题目要求的数值)是为了避免特殊情况溢出
typedef int ElementType;
struct LNode
{
ElementType Data[MAXSIZE];
int Length;
};
typedef struct LNode* List;
定义一个顺序表,保存数组和数组长度。题干给出了数组大小范围:
//初始化顺序表
List initSeqList()
{
List L;
L = (List)malloc(sizeof(struct LNode));
L->Length = 0;
return L;
}
初始化顺序表
//传值
void getValue(List L, int n)
{
List p = L;
int i;
for (i = 0; i < n; i++)
scanf("%d", &(p->Data[i]));
p->Length = n;
}
输入数组元素,不要忘了给数组长度赋值。
//寻找最长连续递增子序列
void getMaxLength(List L, int n)
{
List p = L;
int i, j = 0, lengths = 1; //lengths为1, 表示长度只有头部
int lengthHead = 0; //lengthHead为该连续子列与下一个比较的前者(简称:比较头)
int lengthTail = 1; //lengthTail为该连续子列比较的后者(简称:比较尾)
int oldHead = 0; //每段连续子列的原头部下标
int maxLength; //最大长度的连续子列
int position; //最大长度的头点下标
for (i = 0; i < n; i++)
{
if (p->Data[lengthHead] < p->Data[lengthTail]) //满足连续子列的条件
{
lengthHead++; //比较头 后移一位
lengthTail++; //比较尾 后移一位
lengths++; //该段长度+1
}
else if (p->Data[lengthHead] >= p->Data[lengthTail]) //不满足连续子列的条件
{
if (j == 0) //第一次传值
{
maxLength = lengths; //将该连续子列的长度赋值给length
position = oldHead; //将该连续子列的原头下标赋值给head
j++;
}
if (j == 1) //第二次及以上传值
{
if (maxLength < lengths)
{
maxLength = lengths;
position = oldHead;
}
}
lengthHead = lengthTail; //比较头变比较尾
oldHead = lengthTail; //原头变比较尾
lengthTail++; //比较尾向后移一位
lengths = 1; //长度重新赋值为1
}
}
for (i = 0; i < maxLength; i++) //输出最长子列
{
if (i == 0)
printf("%d", p->Data[position]);
else
printf(" %d", p->Data[position]);
position++;
}
}
求最长连续递增子数列,比如数列 1,2,3,5,4,8,1
最长连续递增子数列是1,2,3,5。长度为4。
连续递增子数列 1,2,3,5的所有子数列的长度都比 1,2,3,5小。
所以比 1,2,3,5更长的连续递增子数列只可能在之后的数组中出现。
所以思路便是:
从数组下标为0的地方开始遍历,找到第一个连续递增子数列,保存它的第一个数字的下标,保存他的长度。
继续遍历之后的数组,找到连续递增子数列,将他的长度和保存的最长的长度比较,保存更长的子数列的长度和第一个数的下标。
我们每次比较两个数,如果前面的数小于后面的数,那么向后继续比较,所以用到了两个变量:
int lengthHead = 0; //lengthHead为该连续子列与下一个比较的前者(简称:比较头)
int lengthTail = 1; //lengthTail为该连续子列比较的后者(简称:比较尾)
因为以上两个变量要进行移动,所以用下面这个变量保存递增数列的下标:
int oldHead = 0; //每段连续子列的原头部下标
用以下两个变量保存最终的结果:
int maxLength; //最大长度的连续子列
int position; //最大长度的头点下标
遍历数组会遇到两种情况:
递增
if (p->Data[lengthHead] < p->Data[lengthTail])
非递增
else if (p->Data[lengthHead] >= p->Data[lengthTail])
这是大概的框架,接下来要实现遇到这两种情况的操作:
如果是递增的,那么lengthHead和lengthTail向后移动,数列长度增加
if (p->Data[lengthHead] < p->Data[lengthTail]) //满足连续子列的条件
{
lengthHead++; //比较头 后移一位
lengthTail++; //比较尾 后移一位
lengths++; //该段长度+1
}
如果不是递增的:
else if (p->Data[lengthHead] >= p->Data[lengthTail]) //不满足连续子列的条件
{
if (j == 0) //第一次传值
{
maxLength = lengths; //将该连续子列的长度赋值给length
position = oldHead; //将该连续子列的原头下标赋值给head
j++;
}
if (j == 1) //第二次及以上传值
{
if (maxLength < lengths)
{
maxLength = lengths;
position = oldHead;
}
}
lengthHead = lengthTail; //比较头变比较尾
oldHead = lengthTail; //原头变比较尾
lengthTail++; //比较尾向后移一位
lengths = 1; //长度重新赋值为1
}
如果是找到的第一个递增数列,那么保存长度和第一个数的位置,j变成1:(只进入一次)
if (j == 0) //第一次传值
{
maxLength = lengths; //将该连续子列的长度赋值给length
position = oldHead; //将该连续子列的原头下标赋值给head
j++;
}
然后进入下面的循环:
if (j == 1) //第二次及以上传值
{
if (maxLength < lengths)
{
maxLength = lengths;
position = oldHead;
}
}
当然,第一个递增子数列进入这个分支,但是不满足 if (maxLength < lengths),退出。
lengthHead = lengthTail; //比较头变比较尾
oldHead = lengthTail; //原头变比较尾
lengthTail++; //比较尾向后移一位
lengths = 1; //长度重新赋值为1
当不满足递增时,lengthHead和lengthTail是下面这种状态:
所以,下一个递增数列的开头便是lengthTail的位置,所以要lengthHead = lengthTail
并且保存新的开头的位置:oldHead = lengthTail
然后移动lengthTail的位置到lengthHead后面的位置:lengthTail++
重置length的长度为1
最后根据保存的数据maxLength和position,输出最大递增子列:
for (i = 0; i < maxLength; i++) //输出最长子列
{
if (i == 0)
printf("%d", p->Data[position]);
else
printf(" %d", p->Data[position]);
position++;
}