我要成为数据结构与算法高手(一)之顺序表

134 阅读7分钟

1. 定义顺序表结构 SeqList

image.png

  • data: 存储数据的数组。
  • index: 数组元素的索引 (下标),从 0 开始。
  • length: 顺序表中当前元素的数量。
#define MAX_SIZE 8

struct SeqList 
{
    int data[MAX_SIZE];      //数据空间
    int length;              //当前长度
};

2. 初始化顺序表 initSeqList(SeqList &L, int arr[], int n)

初始化调用函数:

int Main()
{
		int arr[] = {1,2,3,4,5};
    int n = sizeof(arr)/sizeof(arr[0]);

		SeqList L;
    initSeqList(L, arr, n);
}
void initSeqList(SeqList &L)
{
    L.length = 0;
}

L.length = 0,长度初始化如下:

1.png

void initSeqList(SeqList &L, int arr[], int n)
{
    for (int i = 0; i < n; i++)
    {
        L.data[i] = arr[i];
    }
    L.length = n;
    
}
初始化过程(循环)

2.png

初始化后:

3.png

3. 打印顺序表 (printSeqList(SeqList &L))

假设 L.data = {1, 2, 3,4,5}, L.length = 5):

void printSeqList(SeqList &L)
{
    for (int i = 0; i < L.length; ++i)
    {
        printf("%d ", L.data[i]);
    }
    printf("\n");
}

4.png

4. 尾部插入数据 insertSeqListTail(SeqList &L, int x)

void insertSeqListTail(SeqList &L, int x)
{
    //判断顺序表是否已满
    if (L.length >= MAX_SIZE)
    {
        printf("顺序表已满,无法插入数据\n");
        return;
    }
    //将数据插入到顺序表尾部
    L.data[L.length] = x;  // 将新元素x存入顺序表当前末尾位置
			        // data数组的索引正好等于当前长度值(因为数组从0开始)
			        // 例如当length=3时,元素存储在索引0,1,2,新元素应放在索引3的位置
    L.length++;
}
  • 功能: 在顺序表的末尾插入元素 x
  • 图示 (假设 L.data = {1, 2, 3}L.length = 3x = 4):
插入前:

5.png

插入过程:

6.png

插入后:

7.png

5. 头部插入数据 insertSeqListHead(SeqList &L, int x)

void insertSeqListHead(SeqList &L, int x)
{
    //判断顺序表是否已满
    if (L.length >= MAX_SIZE)
    {
        printf("顺序表已满,无法插入数据\n");
        return;
    }
    //将数据插入到顺序表头部
    //将顺序表中所有元素后移一位
    //第一种方法:
    for (int i = L.length; i > 0; --i)
    {
        L.data[i] = L.data[i-1];        //将i-1位置的元素移到i位置
    }
    //将新元素x存入顺序表头部
    L.data[0] = x;
    L.length++;

    //第二种方法:
    // for (int i = L.length-1; i >= 0; --i)
    // {
    //     L.data[i+1] = L.data[i];     //将i位置的元素移到i+1位置
    // }
    // L.data[0] = x;
    // L.length++;

}
  • 功能: 在顺序表的头部插入元素 x。
  • 图示 (假设 L.data = {1, 2, 3}L.length = 3x = 0):
插入前:
SeqList L:
+--------+-------------------------------------+
| data   | [1] [2] [3] [ ] [ ] [ ] [ ] [ ] |
| index  |  0   1   2   3   4   5   6   7  |
+--------+-------------------------------------+
| length |  3                                  |
+--------+-------------------------------------+
插入过程(元素后移):
for (i = L.length; i > 0; i--)
    L.data[i] = L.data[i - 1];

i = 3:  L.data[3]  <---  L.data[2] (3)
        [1] [2] [3] [ ] ...
                     ^
                     |
                    [3]

i = 2:  L.data[2]  <---  L.data[1] (2)
        [1] [2] [3] [3] ...
                 ^
                 |
                [2]

i = 1:  L.data[1]  <---  L.data[0] (1)
        [1] [2] [2] [3] ...
             ^
             |
            [1]

x (0) ---> L.data[0]
          [ ] [1] [2] [3] ...
           ^
           |
           0

L.length++; (L.length 变为 4)
插入后:
SeqList L:
+--------+-------------------------------------+
| data   | [0] [1] [2] [3] [ ] [ ] [ ] [ ] |
| index  |  0   1   2   3   4   5   6   7  |
+--------+-------------------------------------+
| length |  4                                  |
+--------+-------------------------------------+

6. 在指定位置插入数据 insertSeqListPos(SeqList &L, int pos, int x)

void insertSeqListPos(SeqList &L, int pos, int x)
{
    //判断顺序表是否已满
    if (L.length >= MAX_SIZE)
    {
        printf("顺序表已满,无法插入数据\n");
        return;
    }
    //判断插入位置是否合法
    if (pos < 0 || pos > L.length)
    {
        printf("插入位置不合法\n");
        return;
    }
    //将数据插入到指定位置
    //将顺序表中所有元素后移一位
    for (int i = L.length; i > pos; --i)
    {
        L.data[i] = L.data[i-1];        //将i-1位置的元素移到i位置
    }
    //将新元素x存入指定位置
    L.data[pos] = x;
    L.length++;
}
  • 功能: 在索引 pos 处插入元素 x
  • 图示 (假设 L.data = {1, 2, 3}L.length = 3pos = 1x = 5):
插入前:
SeqList L:
 +--------+-------------------------------------+
 | data   | [1] [2] [3] [ ] [ ] [ ] [ ] [ ] |
 | index  |  0   1   2   3   4   5   6   7  |
 +--------+-------------------------------------+
 | length |  3                                  |
 +--------+-------------------------------------+
插入过程(元素后移):

8.png

插入后:
SeqList L:
+--------+-------------------------------------+
| data   | [1] [5] [2] [3] [ ] [ ] [ ] [ ] |
| index  |  0   1   2   3   4   5   6   7  |
+--------+-------------------------------------+
| length |  4                                  |
+--------+-------------------------------------+

7.删除数据 deleteElem(SeqList &L, int key)

void deleteElem(SeqList &L, int key)
{
    //判断顺序表是否为空
    if (L.length == 0)
    {
        printf("顺序表为空,无法删除数据\n");
        return;
    }
    //先要查找key在顺序表中的位置
    int pos = 0;
    while (pos < L.length && L.data[pos] != key)
    {
        pos++;
    }
    
    if (pos >= L.length)    //判断key是否存在
    {
        printf("删除的key不存在\n");
        return;
    }
    
    //删除数据
    //将顺序表中所有元素前移一位
    for (int i = pos; i < L.length-1; ++i)
    {
        L.data[i] = L.data[i+1];        //将i+1位置的元素移到i位置
    }

    //更新长度
    L.length--;
}
  • 功能: 删除第一个值为 key 的元素。
  • 图示 (假设 L.data = {1, 5, 2, 3}L.length = 4key = 5):
删除前:
SeqList L:
+--------+-------------------------------------+
| data   | [1] [5] [2] [3] [ ] [ ] [ ] [ ] |
| index  |  0   1   2   3   4   5   6   7  |
+--------+-------------------------------------+
| length |  4                                  |
+--------+-------------------------------------+
删除过程(查找和元素前移):

9.png

删除后:
SeqList L:
+--------+-------------------------------------+
| data   | [1] [2] [3] [3] [ ] [ ] [ ] [ ] |
| index  |  0   1   2   3   4   5   6   7  |
+--------+-------------------------------------+
| length |  3                                  |
+--------+-------------------------------------+

8.反转顺序表  reverseSeqList(SeqList &L)

void reverseSeqList(SeqList &L)
{
    //判断顺序表是否为空
    if (L.length == 0)
    {
        printf("顺序表为空,无法反转\n");
        return;
    }
    //反转顺序表
    int left = 0;
    int right = L.length-1;
    while (left < right)
    {
        //交换左右两个元素
        int temp = L.data[left];
        L.data[left] = L.data[right];
        L.data[right] = temp;

        //更新左右指针(更新区间)
        left++;
        right--;
    }
}
  • 功能: 将顺序表中的元素倒序排列。
  • 图示 (假设 L.data = {1, 2, 3, 4}L.length = 4):
反转前:
SeqList L:
+--------+-------------------------------------+
| data   | [1] [2] [3] [4] [ ] [ ] [ ] [ ] |
| index  |  0   1   2   3   4   5   6   7  |
+--------+-------------------------------------+
| length |  4                                  |
+--------+-------------------------------------+
 left = 0
 right = 3
反转过程(循环交换):
while (left < right) {
    temp = L.data[left];
    L.data[left] = L.data[right];
    L.data[right] = temp;
    left++;
    right--;
}

1. left = 0, right = 3:
   交换 L.data[0] 和 L.data[3]:
   [1] [2] [3] [4]  --->  [4] [2] [3] [1]
    ^           ^          ^           ^

2. left = 1, right = 2:
   交换 L.data[1] 和 L.data[2]:
   [4] [2] [3] [1]  --->  [4] [3] [2] [1]
        ^   ^                   ^   ^

3. left = 2, right = 1:  循环条件 (left < right) 不满足,结束。
反转后:
SeqList L:
+--------+-------------------------------------+
| data   | [4] [3] [2] [1] [ ] [ ] [ ] [ ] |
| index  |  0   1   2   3   4   5   6   7  |
+--------+-------------------------------------+
| length |  4                                  |
+--------+-------------------------------------+

9. 排序顺序表  sortSeqList(SeqList &L) - 使用冒泡排序

void sortSeqList(SeqList &L)
{
    int n = L.length;
    //冒泡排序
    for (int i = 0; i < n-1; ++i)
    {
        for (int j = 0; j < n-i-1; ++j)
        {
            if (L.data[j] > L.data[j+1])
            {
                int temp = L.data[j];
                L.data[j] = L.data[j+1];
                L.data[j+1] = temp;
            }
        }
    }
}
  • 功能: 将顺序表中的元素按升序排列。
  • 图示 (假设 L.data = {4, 2, 1, 3}L.length = 4 - 仅展示第一轮):
排序前:
SeqList L:
+--------+-------------------------------------+
| data   | [4] [2] [1] [3] [ ] [ ] [ ] [ ] |
| index  |  0   1   2   3   4   5   6   7  |
+--------+-------------------------------------+
| length |  4                                  |
+--------+-------------------------------------+
排序过程(第一轮冒泡排序):
for (i = 0; i < n - 1; i++) {      // 外层循环 (n = L.length)
    for (j = 0; j < n - i - 1; j++) {  // 内层循环
        if (L.data[j] > L.data[j + 1]) {
            // 交换 L.data[j] 和 L.data[j + 1]
        }
    }
}

i = 0:
  j = 0:  比较 L.data[0] (4) 和 L.data[1] (2)  ->  4 > 2, 交换
          [4] [2] [1] [3]  --->  [2] [4] [1] [3]
   index   0   1   2   3          0   1   2   3
           ^   ^                  ^   ^

  j = 1:  比较 L.data[1] (4) 和 L.data[2] (1)  ->  4 > 1, 交换
          [2] [4] [1] [3]  --->  [2] [1] [4] [3]
   index   0   1   2   3          0   1   2   3
               ^   ^                  ^   ^

  j = 2:  比较 L.data[2] (4) 和 L.data[3] (3)  ->  4 > 3, 交换
          [2] [1] [4] [3]  --->  [2] [1] [3] [4]
   index   0   1   2   3          0   1   2   3
                   ^   ^                  ^   ^
  • 第一轮结束后: L.data = {2, 1, 3, 4} (最大的元素 4 已移至最后)

    后续轮次类似,直到整个顺序表有序。

10. 二分查找 binarySearch(SeqList &L, int key) - 元素有顺序是前提

int binarySearch(SeqList &L, int key)
{
    int left = 0;
    int right = L.length-1;
    while (left <= right)
    {
        int mid = (left + right) / 2;
        if (L.data[mid] == key)
        {
            return mid;
        }
        else if (L.data[mid] < key)
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }
    return -1;
}
  • 功能: 在已排序的顺序表中查找值为 key 的元素。
  • 图示 (假设 L.data = {1, 2, 3, 4, 5}L.length = 5key = 3):
SeqList L (已排序):
+--------+-------------------------------------+
| data   | [1] [2] [3] [4] [5] [ ] [ ] [ ] |
| index  |  0   1   2   3   4   5   6   7  |
+--------+-------------------------------------+
| length |  5                                  |
+--------+-------------------------------------+

left = 0
right = 4
查找过程:
while (left <= right) {
    mid = (left + right) / 2;
    if (L.data[mid] == key) {
        return mid;  // 找到,返回索引
    } else if (L.data[mid] < key) {
        left = mid + 1;  // 在右半部分查找
    } else {
        right = mid - 1; // 在左半部分查找
    }
}
return -1; // 未找到

1. mid = (0 + 4) / 2 = 2
   L.data[2] (3) == key (3)  ->  返回 2 (找到)

如果 key = 6 (未找到):

  1. mid = 2, L.data[2] (3) < 6, left = 3
  2. mid = 3, L.data[3] (4) < 6, left = 4
  3. mid = 4, L.data[4] (5) < 6, left = 5
  4. left (5) > right (4), 循环结束, 返回 -1

11. 合并有序顺序表  mergeSeqList(SeqList &L1, SeqList &L2, SeqList &L)

void mergeSeqList(SeqList &L1, SeqList &L2, SeqList &L)
{
    int i = 0;  //L1的索引
    int j = 0;  //L2的索引
    int k = 0;  //L的索引
    while (i < L1.length && j < L2.length)
    {
        if (L1.data[i] <= L2.data[j])
            L.data[k++] = L1.data[i++]; //将L1的元素存入L
        else
            L.data[k++] = L2.data[j++]; //将L2的元素存入L
    }

    while (i < L1.length)
        L.data[k++] = L1.data[i++]; //合并剩余的L1元素 因为有时会两组数据长度不一样,有一组数据合并完了,另一组数据还没有合并完,所以要将剩余的元素合并到L中

    while (j < L2.length)
        L.data[k++] = L2.data[j++]; //合并剩余的L2元素

    L.length = k; //更新L的长度

}
  • 功能: 将两个已排序的顺序表 L1 和 L2 合并到 L。
  • 图示 (假设 L1.data = {1, 3, 5}, L1.length = 3L2.data
合并前:
i = 0 (L1 的索引)
j = 0 (L2 的索引)
k = 0 (L 的索引)

1. L1[0] (1) <= L2[0] (2)  ->  L[0] = 1, i++, k++
2. L1[1] (3) > L2[0] (2)   ->  L[1] = 2, j++, k++
3. L1[1] (3) <= L2[1] (4)  ->  L[2] = 3, i++, k++
4. L1[2] (5) > L2[1] (4)   ->  L[3] = 4, j++, k++
5. L1[2] (5) <= L2[2] (6)  ->  L[4] = 5, i++, k++
6. i == L1.length, 循环1结束

   剩余L2[2] (6) -> L[5] ,j++ ,k++
   j == L2.length, 循环2结束, 循环3结束
合并后:
  L:  [1] [2] [3] [4] [5] [6] [ ] [ ]
       0   1   2   3   4   5   6   7
  L.length = 6