1. 线性顺序表的定义
结构体定义的经典定义方式,必备
#define MAX_SIZE 100
typedef int ElementType;
typedef struct sqlist{
ElementType data[MAX_SIZE];
int length;
} SqList;
2. 分配空间
SqList * L->data = (ElementType *) malloc(sizeof(ElementType) * MAX_SIZE);
3.顺序表的查找
查找元素e在表中的位置,如果查找到就返回数组下标加1,没有知道就返回-1
// 按值查找时,只能顺序查找
int FindByValue(SqList *L, int e){
int i;
for(i = 0; i < L->length; i++)
if(L->data[i] == e)
return i + 1;
return -1;
}
4. 顺序表的插入
在顺序表L的第i个位置(1<= i <= n)个位置上插入一个新结点e。插入思路如下:
- 将线性表L中的第i个至第n个节点向后移动一个位置;
- 将结点e插入到 i-1位置;
- 线性长度加1。
bool ListInsert(SqList *L, int i, int e){
if (i<1 || i>L->length + 1) return false;
if (L->length >= MAX_SIZE) return false;
for (int j = L->length; j >= i; j--)
L->data[j] = L->data[j-1];
L->data[i-1] = e;
L ->length++;
return true;
}
5.线性表的删除
5.1 按照位置进行删除
在顺序表L中删除第i个位置(1<= i <= n)。思路如下:
- 将线性表L中的第i + 1个至第n个节点依次向前移动一个位置;
- 线性长度减1。
bool ListDelete(SqList *L, int i){
if (L->length === 0) return false;
if (i < 1 || i > L->length) return false;
for (int j = i; j < L->length; j++)
L->data[j-1] = L->data[j];
L ->length--;
return true;
}
5.2 按照值进行删除
在顺序表L中删除值为x的结点。当表n较大时,算法的效率相当低,因此算法的平均时间复杂度为O(n)。思路如下:
- 从线性表L中的查找值为x的第一个数据元素;
- 将从找到的位置至最有一个结点依次向前移动一个位置;
- 线性长度减1。
bool Locate_Delete_SqList(SqList *L, int x){
int i = 0, k;
while (i < L->length){
if(L->data[i] != x) i++;
else{
for (k = i; k < L->length; k++)
L->data[k - 1] = L->data[k];
L->length--;
break;
}
}
if (i >= L->length) {
printf("要删除的数据元素不存在!\n");
return false;
}
return true;
}
6. 顺序表的经典算法题
6.1 双指针不回溯
题目描述:
编写算法,将两个非递减有序顺序表A和B合并成一个新的非递减有序顺序表C,已知顺序表A和B的元素个数分别为m和n。其中顺序表采用动态分配空间,定义如下:
Typedef struct{
ElementType *elem;
int length;
int listsize;
}SqList;
解题思路:
要合并两个有序的顺序表A和B,那么生成一个数据空问C,当A和B都未到末尾时,逐个比较A和B的元素,将值小的元素放入到C中,当元索相等时,只保留一个。当A或B结束时,将未结束表的元素逐个放入到C中。
解题代码:
void Combination(SqList * L1, SqList * L2,SqList * L3){
int i=j=k=0;
while((i!=L1->length) && (j!=L2->length)){
if(L1->data[i] > L2->data[j]){
//L1中元素大,合并L2中元素到L3中
L3->data[k++] = L2->data[j++];
}else if(L1->data[i] == L2->data[j]){
//两元素值相等
L3->data[k++] = L2->data[j++];
i++;
}else{
//L2中元素大
L3->data[k++] = L1->data[i++];
}
L3->length++;
}
while(i < L1-->length){
//L2结束,合并L1中元素到L3末尾
L3->data[k++] = L1->data[i++];
L3->length++;
}
while(j < L2-->length){
//L1结束,合并L2中元素到L3中
L3->data[k++] = L2->data[j++];
L3->length++;
}
}
6.2 顺序表的逆序问题
题目描述:
试写一算法,实现顺序表的就地逆置,即利用原表的存储空间将线性表逆置。
解题思路:
要实现数组的逆置,且要求使用原有空间,只需要将对应的数组元素位置交换就可以,如第1个(对应下标是0)和第n个(对应下标是n-1)交换,第2个(对应下标是1)和第n一1个(对应下标是n-2)交换,需要交换的两个元素的下标之和是n-1,其中n是表长。
实现代码如下:
int ListOppose(SqList *L){
//交换元素的首尾位置
int i;
ElementType x;
for(i = 0; i < L->length / 2; i++){
x = L->data[i];
L->data[i] = L->data[L->length - i - 1]; //数据元素交换位置
L->data[L->length - i - 1] = x; //数组长度加1
}
return 1;
}
6.3 左右指针法
题目描述:
一个线性表中的元素全部为正或负整数,试设计一算法,在尽可能少的时间内重排该表,将正、负整数分开,使线性表中所有负整数在正整数前面。
解题思路:
采用快速排序的思想,将首部第一个元素(会被替换,因此比较结束后,需要将该元素放在合适位置)作为暂存值,列表中每个元素与0进行比较,判断对应的元素是否是正数或者负数,然后放到对应位置。
实现代码:
int ReArrange(ElementType * a, int n){
int low = 0, high = n - 1;
int t = a[low];
int j;
while(low < high){
// 内部两个while循环不可交换位置,枢轴元素在首部,应先比较高位,低位置先排序好
/*while (low < high && a[low] < 0) { // 若要先比较低位,则枢轴元素应为尾部
low++;
}
a[high] = a[low];*/
while (low < high && a[high] >= 0){
//对高位进行比较,符合条件进行自减
high--;
}
a[low] = a[high]; // 优先对低位进行规整
while (low < high && a[low] < 0) {
//对低位进行比较,符合条件进行自增
low++;
}
a[high] = a[low];
}
a[low] = t; //将枢轴元素放在最终的位置
}
6.3.1 奇偶数区分
一个线性表中的元素全部为整数,设计一算法,在尽可能少的时间内重排该表,将奇数和偶数分开,使线性表中所有奇数在偶数前面。
// 方法1
void part_method1(int *a, int n){
int t, low = 0, high = n - 1;
t = a[low];
while (low < high){
while ((low < high) && (a[high] % 2 == 0)){
high--;
}
a[low] = a[high];
while((low < high) && (a[low] % 2 != 0)){
low++;
}
a[high] = a[low];
}
a[low] = t;
}
//方法2
void part_method2(int * a, int n){
int t, low = 0, high = n - 1;
while (low < high){
while ((low < high) && (a[high] % 2 == 0)){
high--;
}
while((low < high) && (a[low] % 2 != 0)){
low++;
}
if (low < high){
t = a[low];
a[low] = a[high];
a[high] = t;
}
}
}
6.4 数据元素的旋转
6.4.1 例子1
题目描述:
已知在一维数组A[0,…,m十n一1]中依次存放着两个顺序表:(a1,a2,…,am)和(b1,b2,…,bn)。试编写程序,将数组中两个顺序表的位置互换,即将(b1,b2,…,bn)放在(a1,a2,…,am)的前面。
解题思路:
- 将整个数组全部进行逆序;
- 将逆序后的数组(b1,…,bn)进行逆序;
- 将逆序后的数组(a1,…,am)进行逆序。
实现代码如下:
typedef int ElementType;
void Reverse(ElementType arr[], int left, int right, int arr_size){
// 输入数组,需要交换的左边索引,需要交换的右边索引,及数组的大小
if ((left >= right) || (right >= arr_size))
return;
int mid = (left + right) / 2;
for(int i = 0; i <= mid - left; i++){
ElementType temp = arr[left + i];
arr[left + i] = arr[right - i];
arr[right - i] = temp;
}
}
void Exchange(ElementType arr[],int m, int n, int arr_size){
//注意,参数中的m和n是两数组中元素个数
Reverse(arr, 0, arr_size - 1, arr_size);
Reverse(arr, 0, n - 1, arr_size);
Reverse(arr, n, arr_size - 1, arr_size);
}
6.4.2 例子2
题目描述:
设有一个线性表存放在一维数组α[0,…,n-1]中,编写一算法,将数组中每个元素循环右移k位,要求只用
一个辅助单元,时间复杂度为O(n)。
解题思路:
1.先将所有元素进行逆序;
2.右移k位,则会有k%n个元素会被移动至头部,对这部分数据元素进行逆序;
3.将剩余的n-(k%n)个元素进行逆序排序。
实现代码如下:
typedef int ElementType;
void Reverse(ElementType arr[], int p, int q){
// 输入数组,需要交换的左边索引,需要交换的右边索引,并进行交换
for(;p < q; p++, q--){
ElementType temp = arr[p];
arr[p] = arr[q];
arr[q] = temp;
}
}
void RightShift(ElementType arr[], int n, int k){
k = k % n; //k%n个元素,会被移动到头部,剩下的元素向后移动
Reverse(arr, 0, n - 1);
Reverse(arr, 0, k - 1);
Reverse(arr, k, n - 1);
}