线性表
Posted 阿呆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性表相关的知识,希望对你有一定的参考价值。
线性表是由 \\(n\\)个元素\\((n \\ge 0)\\)组成的有限序列,线性表的特征
- 所有数据元素类型相同
- 线性表是由有限个元素构成
- 线性表中的数据元素是与位置是有关的,这一点表明线性表不同于集合,线性表中每个元素都有一个对应的序号,线性表中的元素可以重复出现。
线性表的逻辑结构一般表示为:\\( a_1,a_2,...,a_{i-1},a_i,a_{i+1},...,a_n \\)
- 除起始元素没有\\(a_1\\) 没有前驱元素之外,其他元素\\(a_i\\)有且仅有一个前驱元素\\(a_{i-1},\\)
- 除终端元素没有后继元素外,其他元素 \\(a_i\\) 有且仅有一个后继元素\\(a_{i+1}\\)
线性表是一种逻辑结构,线性表的存储结构有
- 顺序存储
- 链式存储
顺序表
顺序表的特点
- 属于直接映射(逻辑上相邻的元素,其物理位置也相邻),
- 具有随机存取特性( 通过首地址和元素序号可以在 \\(O(1)\\) 时间内找到指定元素 )
- 删除和插入元素需要移动大量元素。
- 插入元素移动的平均次数\\(=\\frac{1}{n}\\sum_{i=1}^{n}(n-i+1)=\\frac{n}{2}\\), 因此时间复杂度为\\(O(n)\\)
- 删除元素移动的平均次数\\(=\\frac{1}{n}\\sum_{i=1}^{n}(n-i)=\\frac{n-1}{2}\\),因此时间复杂度为 \\(O(n)\\)
- 存储密度高,其值为\\(1\\)。\\(存储密度=节点数据本身所占用的存储量/节点结构占用的存储量\\)
描述顺序结构的三个属性
- 顺序表的起始地址
- 顺序表的最大长度
- 顺序表的当前长度
# define MaxSize 100
typedef struct
{
int data[MaxSize];
int length;
} SqList;
给定一个有 \\(n\\) 个元素的整型数组\\(arr\\),其中连续的相等元素构成的子序列称为平台,设计一个算法求 \\(arr\\)中最长平台的长度。
int maxLength(int arr[], int n)
{
int len = 1;
int max = 1;
int start = 0;
for (int i = 1; i < n; i++)
{
if (arr[i] == arr[start])
len++;
else
{
if (len > max)
max = len;
start = i;
len = 1;
}
}
if (len > max)
max = len;
return max;
}
设有一个顺序表\\(L\\), 其元素为整型数据,设计一个算法将\\(L\\)中所有小于\\(0\\)的整数放在前半部分,大于等于\\(0\\)的整数放在后半部分。
void change(SqList &L)
{
int tmp;
int i = 0, j = L.length - 1;
while (i < j)
{
while (i < j && L.data[i] < 0)
i++;
while (i < j && L.data[j] >= 0)
j--;
if (i < j)
{
tmp = L.data[i];
L.data[i] = L.data[j];
L.data[j] = tmp;
}
}
}
设有一个顺序表\\(L\\),其元素为整型。设计一个尽可能高效的算法,以第一个元素为分界线,将所有小于等于它的元素移到该元素前面,将所有大于它的元素移到该元素的后面。
void move(SqList &L)
{
int i = 0;
j = L.length - 1;
int pivot = L.data[0];
int tmp;
while (i < j)
{
while (i < j && L.data[j] > pivot)
j--;
while (i < j && L.data[i] <= pivot)
i++;
tmp = L.data[i];
L.data[i] = L.data[j];
L.data[j] = tmp;
}
if (L.data[i] < pivot)
{
tmp = L.data[0];
L.data[0] = L.data[i];
L.data[i] = tmp;
}
}
void move(SqList &L)
{
int i = 0, j = L.length - 1;
int pivot = L.data[0];
while (i < j)
{
while (i < j && L.data[i] > pivot)
j--;
L.data[i] = L.data[j];
i++;
while (i < j && L.data[i] <= pivot)
i++;
L.data[j] = L.data[i];
j--;
}
L.data[i] = pivot;
}
void move(SqList &L)
{
int i = 0;
int pivot = L.data[0];
int tmp;
for (int j = 1; j < L.length; j++)
{
i++;
if (i != j)
{
tmp = L.data[i];
L.data[i] = L.data[j];
L.data[j] = tmp;
}
}
tmp = L.data[0];
L.data[0] = L.data[i];
L.data[i] = tmp;
}
已知长度为 \\(n\\) 的线性表 \\(L\\) 采用顺序存储结构,编写一个时间复杂度为\\(O(n)\\),空间复杂度为\\(O(1)\\) 的算法,该算法删除线性表中所有值为\\(x\\)的数据元素。
void deleteSameElements(SqList &L, int x)
{
int base = 0;
for (int i = 0; i < L.length; i++)
{
if (L.data[i] != x)
{
L.data[base] = L.data[i];
base++;
}
}
L.length = base;
}
void deleteSameElements(SqList &L, int x)
{
int count = 0;
int i = 0;
while (i < L.length)
{
if (L.data[i] == x)
count++;
else
L.data[i - count] = L.data[i];
i++;
}
L.length = L.length - count;
}
已知长度为 \\(n\\) 的线性表 \\(L\\) 采用顺序存储结构,试设计一个时间复杂度空间复杂度两方面都尽可能高效的算法,该算法删除线性表中元素值为\\([x,y]\\)之间的所有数据元素。
单链表
单链表是用任意一组存储单元存放线性表中的元素,每个节点通过一个指针指向后继节点。这组存储单元可以是连续的也可以是不连续的。
- 通过头节点(带头节点的单链表)或首节点(不带头节点的单链表)的指针来标识一个链表
- 从一个已知节点出发,只能访问该节点和通过\\(next\\)指针访问其后继节点,无法直接找到该节点之前的其他节点
- 在单链表中插入一个节点或者删除一个节点必须已知其前驱节点,插入和删除操作不需要移动节点
typedef struct LinkedNode
{
T data; //T表示数据类型
struct LNode* next;
}LinkedList;
建立单链表的两种方式
头插法「逆序了」
void creatLinkedList(LinkedList* &L, int a[],int n) { LinkedList* s; L=(LinkedList*)malloc(sizeof(LinkedList)); L->next=NULL; for(int i=0;i<n;i++) { s=(LinkedList*)malloc(sizeof(LinkedList)); s->data=a[i]; s->next=NULL; L->next=s; } }
尾插法「还是原来的顺序」
void creatLinkedList(LinkedList* &L, int a[],int n) { LinkedList* s,*r; L=(LinkedList*)malloc(sizeof(LinkedList)); r=L; //r始终指向终端节点,开始时指向头结点 for(int i=0;i<n;i++) { s=(LinkedList*)malloc(sizeof(LinkedList)); s->data=a[i]; r->next=s; r=s; } r->next=NULL; }
单链表的基本操作
按序号查找节点值算法
int findNode(LinkedList *L,int i,int &e) { int j=0; LinkedList *ptr=L; while(ptr!=NULL && j<i) { j++; ptr=ptr->next; } if(NULL==ptr) return 0; else { e=ptr->data; return 1; } }
按元素值查找序号算法
int findNode(LinkedList *L,int key) { LinkedList *ptr=L->next; int index=0; while(ptr!=NULL && ptr->data!=key) { index++; ptr=ptr->next; } if(ptr!=NULL) return index; else return 0; }
插入元素:将值为\\(x\\)的元素的新节点插入到第\\(i\\)个节点的位置上,即先在单链表中找到插入节点的前驱节点,即第\\(i-1\\)个节点,再在其后插入新节点。
s->next=p->next; p->next=s;
删除元素:将单链表中的第 \\(i\\) 个节点删除。
q=p->next; p->next=q->next; free (q);
有一个线性表\\((a_1,a_2,...,a_n)\\) 采用带头节点的单链表\\(L\\)存储,设计一个就地算法将其就地逆置,所谓“就地”是指算法的辅助空间为\\(O(1)\\).
void reverse(LinkedList * & L)
{
LinkedList *ptr=L->next;
LinkedList *q;
L->next=NULL;
while(ptr!=NULL)
{
q=ptr->next;
ptr->next=L->next;
L->next=ptr;
ptr=q;
}
}
设\\(C=\\{a_1,b_1,a_2,b_2,...,a_n,b_n\\}\\) 为一线性表,采用带头节点的\\(hc\\)单链表存放,设计一个就地算法,将其拆分为两个线性表(它们都是用单链表存放)使得\\(A=\\{a_1,a_2,...,a_n\\},B=\\{b_1,b_2,...,b_n\\}\\)
void split(LinkedList *hc,LinkedList *&ha,LinkedList *&hb)
{
LinkedList *r=ha;
LinkedList *p=hc->next;
LinkedList *q;
hb->next=NULL;
while(p!=NULL)
{
r->next=p;
r=p;
p=p->next;
q=p->next;
p->next=hb->next;
hb->next=p;
p=q;
}
r->next=NULL; //别忘了
}
王道题讲解
有一个带头结点的单链表\\(L\\),设计一个算法使其元素递增有序。
void sort(LinkedList * &L)
{
LinkedList *p=L->next;
LinkedList *q=p->next;
LinkedList *pre,tmp;
p->next=NULL: //含有一个元素的有序单链表
p=q;
while(p!=NULL)
{
q=p->next;
pre=L; //单链表只能从头开始往后寻找节点
tmp=pre->next;
while(tmp!=NULL && tmp.data < p->data)
{
pre=tmp;
tmp=tmp->next;
}
p->next=pre->next;
pre->next=p;
p=q;
}
}
给定两个单链表,编写算法找出其公共的节点
分析:从头到尾扫描单链表\\(A\\),判断当前元素是否在单链表\\(B\\)中出现,若在则插入到单链表\\(C\\)中。
void findSameNode(LinkedList *A,LinkedList *B,LinkedList *&C)
{
LNode *p=A->next;
LNode *q=B->next;
LNode *r;
C=(LNode*)malloc(sizeof(LNode));
C->next=NULL;
r=C;
while(p!=NULL)
{
q=B->next;
while(q!=NULL && q.data!=p.data)
q=q->next;
if(q!=NULL)
{
r->next=p;
r=p;
}
p=p->next;
}
}
\\(2010\\)年联考真题
设将\\(n\\) 个整数存放到一维数组R中。试设计一个在时间和空间尽可能高的算法。将\\(R\\)中保存的序列循环左移\\(p (0 \\lt q\\lt n)\\)个位置,即将\\(R\\)中的数据由\\((X_0,X_1,...,X_{n-1})\\) 变换为\\((X_p,X_p+1,...,X_{n-1},X_0,X_1,....,X_{p-1})\\)
void reverse(int R[],int left,int right)
{
int i=left,j=right;
int tmp;
while(i<j)
{
tmp=R[i];
R[i]=R[j];
R[j]=tmp;
i++;
j--;
}
}
void leftShift(int R[],int n,int p)
{
if(p>0 &&p<n)
{
reverse(R,0,n-1);
reverse(R,0,n-p-1);
reverse(R,n-p,n-1);
}
}
\\(2011\\)年联考真题
给定两个数组\\(A\\)和\\(B\\),数组的长度为\\(n\\),两个数组都分别有序,求出两个数组中的所有数的中位数。
int search(int A[],int B[],int n)
{
int i,j,k;
i=j=k=0;
while(i<n &&j<n)
{
k++;
if(A[i]<B[j])
{
if(k==n)
return A[i];
i++;
}
else
{
if(k==n)
return B[j];
j++;
}
}
}
分别求出两个升序序列\\(A\\),\\(B\\)的中位数,记为\\(a\\),\\(b\\)。若\\(a=b\\),则\\(a\\)或\\(b\\)即为所求,否则舍弃\\(a,b\\)中较小者所在序列的较小一半,同时舍弃较大者所在序列的较大一半,要求两次舍弃的元素个数相同(每次从左侧和右侧删除相同个数的元素后,新的两个数组,它们的中位数与原始数组的中位数是相同的)。重复上述过程,直到两个序列均只含一个元素为止,则较小的即为所求的中位数。
int searchMid(int A[],int B[],int n)
{
int startA,midA,endA;
int startA,midB,endB;
startA=0; endA=n-1;
startB=0; endB=n-1;
while(startA!=endA || startB!=endB)
{
midA=(startA+endA)/2;
midB=(startB+endB)/2;
if(A[midA]==B[midB])
return A[midA];
if(A[midA]<B[midB])
{
if((startA+endA)%2==0)//若元素个数为奇数时
{
startA=midA; //舍弃A中间点以前的部分且保留中间点
endB=midB; //舍弃B中间点以后的部分且保留中间点
}
else //若元素个数为偶数时
{
startA=midA+1; //舍弃A的前半部分,每次舍弃的长度相同,可以保证同时到达
endB=midB; //舍弃B的后半部分
}
}
else if(A[midA] > B[midB])
{
if((startA+endA)%2==0)//若元素个数为奇数时
{
endA=midA; //舍弃A中间点以后的部分且保留中间点
startB=midB;//舍弃B中间点以前的部分且保留中间点
}
else //若元素个数为偶数时
{
endA=midA; //舍弃A的后半部分
startB=midB+1; //舍弃B的前半部分
}
}
}
return A[startA]<B[startB]?A[startA]:B[startB];
}
- 若\\(A\\)和\\(B\\)数组的长度为\\(2k+1\\),取\\(A\\)数组的中位数为\\(A[k]\\),\\(B\\)数组的中位数为\\(B[k]\\),\\(A\\)和\\(B\\)组合起来的中位数应该是第\\(2k+1\\)大的那个数。如果\\(A[k]==B[k]\\),则\\(A[k]\\)必定为第\\(2k+1\\)大的数,是所有数字的中位数。如果\\(A[k]\\gt B[k]\\),则\\(A[k]\\)至少为第\\(2k+2\\)大的数,\\(B[k]\\)至多为第\\(2k+1\\)大的数,中位数介于\\(B[k]\\)和\\(A[k]\\)之间。
- 若\\(A\\)和\\(B\\)数组的长度为\\(2k\\),按照题目所述条件,则\\(A\\)的中位数为\\(A[k-1]\\),则\\(B\\)的中位数为\\(B[k-1]\\),\\(A\\)和\\(B\\)组合起来的中位数应该是第\\(2k\\)大的那个数,若\\(A[k-1]==B[k-1]\\),则\\(B[k-1]\\)必为第\\(2k\\)大的那个数,即所有数字的中位数。如果\\(A[k-1]\\gt B[k-1]\\),则\\(A[k-1]\\)至少为第\\(2k\\)大的数,\\(B[k-1]\\)至多为第\\(2k-1\\)大的数,中位数介于\\(B[k-1]\\)和\\(A[k-1]\\)之间。
\\(2013\\)年联考真题

主元素问题方法一:对数组中元素进行计数,然后查看出现次数最多的元素,若次数大于一半,则为主元素。这种方式只需要对数组扫描一遍,时间复杂度为\\(O(n)\\),空间复杂度为\\(O(n)\\)。
int mainElement(int A[],int n)
{
int *p=(int*)malloc(sizeof(int)*n);
int max=0;
for(int i=0;i<n;i++) p[i]=0;
for(int i=0;i<n;i++)
{
p[A[i]]++;
if(p[A[i])>p[A[max]]) max=A[i];
}
if(p[max]>(n/2) return max;
else return -1;
}
使用快速排序,然后统计相同元素出现的最大次数
void quickSort(int A[],int left,int right)
{
int i=left,j=right;
int tmp;
if(i<j)
{
tmp=A[i];
while(i<j)
{
while(i<j && A[j]>tmp) j--;
A[i]=A[j];
i++;
while(i<j && A[i]<=tmp) i++;
A[j]=A[i];
j--;
}
A[i]=tmp;
quickSort(A,left,i-1);
quickSort(A,i+1,right);
}
}
//然后使用平台算法
数组中存在主元素时,所有的非主元素个数和必少于一半。如果让主元素与一个非主元素"配对“,则最后多出来的元素(没有元素与之配对)就是主元素。从前往后扫描数组元素,假定遇到的当前值选定为主元素,再次遇到它时计数加1,遇到不等的值时,计数减1。当计数减为0后,将遇到的下一个值重新选定为主元素。扫描完毕,当前选定的元素(计数值大于0)可能是主元素,但未必是主元素。还需要对数组再进行一次扫描,记录它出现的实际个数,以判定它是否是主元素。
void mainElemment(int A[],int n)
{
int base=A[0];
int count=1;
int count2=0;
for(int i=1;i<n;i++)
{
if(A[i]==base)
count++;
else
{
if(count>0) count--;
else
{
base=A[i];
count=1;
}
}
}
if(count>0)
{
count2=0;
for(int i=0;i<n;i++)
{
if(base==A[i])
count2++;
}
}
if(count2>(n/2)) return base;
else return -1;
}
以上是关于线性表的主要内容,如果未能解决你的问题,请参考以下文章
在android中的类内的对话框片段的线性布局中添加textview