Sequence table implementation and exercises

Miscellaneous talk:

Some textbooks/tutorials on data structures (implemented in C language) will use the syntax of references in C++. References are indeed simpler in form than pointers. This is simply to avoid the subsequent use of secondary pointers.

I think that since C language is used to implement data structures, pointers should not be the threshold. Is the added reference to C C or C++? If you use C++, you can completely use classes to implement data structures instead of using C-compatible low-level syntax.

Important prerequisite knowledge for implementing data structures in C language: pointers, structures, dynamic memory management, (recursion, function stack frames...).

Sequence table implementation (dynamic version)

Use C to implement the sequence table structure (dynamic version, support automatic expansion), and related operation functions: initialization, destruction, printing, insertion (any position, head insertion, tail insertion), deletion (any position, head deletion, tail deletion), Find and modify.

Note: The function naming in this article (including subsequent data structure implementation) adopts the C++STL naming style.

Define sequence list and function declaration

Define the sequence list structure and declare related functions in the SeqList.h header file:

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

typedef int SLDataType; // 顺序表数据类型
typedef struct SeqList  // 顺序表结构
{
	SLDataType* a; // 动态数组,存放数据元素
	int size;      // 元素个数
	int capacity;  // 数组容量
}SL;

void SLInit(SL* psl);   // 顺序表初始化
void SLDestroy(SL* psl);// 销毁顺序表

// 对数据的管理:增删查改 
void SLPrint(SL* psl); // 打印顺序表
void SLPushBack(SL* psl, SLDataType x); // 尾插元素
void SLPushFront(SL* psl, SLDataType x);// 头插元素
void SLPopFront(SL* psl);// 头删
void SLPopBack(SL* psl); // 尾删

// 顺序表查找
int SLFind(SL* ps, SLDataType x);
// 顺序表在pos位置(下标)插入x
void SLInsert(SL* ps, int pos, SLDataType x);
// 顺序表删除pos位置(下标)的值
void SLErase(SL* ps, int pos);
// 顺序表修改pos位置的值
void SLModify(SL* psl, int pos, SLDataType x);

function definition

Define sequence list operation functions in the SeqList.c file. To facilitate demonstration, all functions will be displayed separately:

initialization

#include "SeqList.h"
void SLInit(SL* psl)
{
	assert(psl);// 断言psl是否为空指针

	psl->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);// 初始容量设置为4
	if (psl->a == NULL) //动态申请失败,结束函数
	{
		perror("malloc fail");
		return;
	}

	psl->capacity = 4;
	psl->size = 0;
}

destroy

Note: The destruction of the sequence table pointer should be operated by the user free(psl); psl = NULL;

void SLDestroy(SL* psl)
{
	assert(psl);

	free(psl->a); // 释放动态数组
	psl->a = NULL;// 指针置空
	psl->size = psl->capacity = 0;// 个数、容量置0
}

Print

void SLPrint(SL* psl)
{
	assert(psl);

	for (int i = 0; i < psl->size; i++)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\n");
}

Check capacity

If the capacity is not enough, expand the capacity by 2 times

void SLCheckCapacity(SL* psl)
{
	assert(psl);

	if (psl->size == psl->capacity)// 顺序表已满,需要扩容
	{
		SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType) * psl->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		psl->a = tmp;
		psl->capacity *= 2;
	}
}

insert

void SLInsert(SL* psl, int pos, SLDataType x)
{
	assert(psl);

	assert(0 <= pos && pos <= psl->size);// 断言pos是否合法,可在原范围[0,size)和下一位置size插入

	SLCheckCapacity(psl);

	int end = psl->size - 1;
	while (end >= pos) 
	{
		psl->a[end + 1] = psl->a[end];// 元素向后移动一位
		--end;
	}

	psl->a[pos] = x;// pos位置插入x
	psl->size++;
}

delete

void SLErase(SL* psl, int pos)
{
	assert(psl);

	assert(0 <= pos && pos < psl->size);// 只能删除原范围数据[0,size)

	int start = pos + 1;
	while (start < psl->size)
	{
		psl->a[start - 1] = psl->a[start];// 元素向前移动一位
		++start;
	}

	psl->size--;
}

tail plug

Add an element to the end of the sequence list

void SLPushBack(SL* psl, SLDataType x)
{
	assert(psl);

	SLInsert(psl, psl->size, x);
}

Head plug

Insert an element before the first element in the sequence list

void SLPushFront(SL* psl, SLDataType x)
{
	assert(psl);

	SLInsert(psl, 0, x);
}

tail delete

Delete the last element of the sequence list

void SLPopBack(SL* psl)
{
	assert(psl);

	SLErase(psl, psl->size - 1);
}

Header deletion

Delete the first element of the sequence list

void SLPopFront(SL* psl)
{
	assert(psl);

	SLErase(psl, 0);
}

Find

Find the first position of the element and return its subscript (returns -1 if not found)

int SLFind(SL* psl, SLDataType x)
{
	assert(psl);

	for (int i = 0; i < psl->size; i++)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}

	return -1;
}

Revise

void SLModify(SL* psl, int pos, SLDataType x)
{
	assert(psl);

	assert(0 <= pos && pos < psl->size);

	psl->a[pos] = x;// 修改pos位置数据
}

test

Define the function in the test.c file or directly test the sequence table function in the main function. It is recommended to perform relevant tests every time a part of the function is implemented. If you implement all the operation functions of the sequence table and then test it, it will not be easy to find errors.

1 Tail plug test

void TestSeqList1()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);

	SLPrint(&s);

	SLDestroy(&s);
}

operation result:

2 plug test

void TestSeqList2()
{
	SL s;
	SLInit(&s);

	SLPushFront(&s, 1);
	SLPushFront(&s, 2);
	SLPushFront(&s, 3);
	SLPushFront(&s, 4);
	SLPushFront(&s, 5);

	SLPrint(&s);

	SLDestroy(&s);
}

operation result:

3 Tail deletion test

void TestSeqList3()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);

	SLPrint(&s);

	SLPopBack(&s);
	SLPopBack(&s);
	SLPopBack(&s);

	SLPrint(&s);

	SLDestroy(&s);
}

operation result:

4 head deletion test

void TestSeqList4()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);

	SLPrint(&s);

	SLPopFront(&s);
	SLPopFront(&s);

	SLPrint(&s);

	SLDestroy(&s);
}

operation result:

5 Insert test

void TestSeqList5()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);

	SLPrint(&s);

	SLInsert(&s, 3, 30);
	SLPrint(&s);

	SLDestroy(&s);
}

operation result:

6 Delete test

void TestSeqList6()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);

	SLPrint(&s);

	SLErase(&s, 2);
	SLPrint(&s);

	SLDestroy(&s);
}

operation result:

7 Find and modify tests

void TestSeqList7()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);

	SLPrint(&s);

	int pos = SLFind(&s, 3);
	if (pos != -1)
	{
		SLErase(&s, pos);
	}
	SLPrint(&s);

	SLDestroy(&s);
}

operation result:

Question practice (increased from time to time)

1. Remove all elements val in the array in place.

Question link: Remove elements

method 1:

  1. Traverse the array nums from front to back and find the first val
  2. Move the element after val forward one position, that is, delete the val; numsSize-1
  3. Repeat the above operation in a loop until all vals are deleted
  4. Return numSize

Time complexity: O(n^2) Space complexity: O(1)

Illustration:

Code:

int removeElement(int* nums, int numsSize, int val)
{
    for(int i = 0; i < numsSize; i++) // 遍历数组
    {
        if(nums[i] == val)// 找到val
        {
            for(int j = i;j < numsSize-1; j++)
            {
                nums[j] = nums[j+1];// val后面元素向前移动一位
            }

            i--; // val下一个元素可能也是val,需要重新判断该位置(图解省略了这种情况)
            numsSize--;// 数组个数-1
        }    
    }

    return numsSize;
}

Method 2:

  1. Set the variable count to record the number of val in the array nums
  2. Traverse the array, for each element in the array, if the element = val then count++, otherwise move the element forward count positions (because the first count vals have been deleted, the subsequent elements need to be overwritten forward)
  3. Return numsSize-count

Time complexity: O(n) Space complexity: O(1)

Illustration:

Code:

int removeElement(int* nums, int numsSize, int val){
    int count = 0;
    for(int i = 0; i < numsSize; i++)
    {
        if(nums[i] == val)
        {
            count++;
        }
        else
        {
            nums[i-count] = nums[i];
        }
    }


    return numsSize - count;
}

Method 3: Double pointers

  1. Use count to record the number of vals
  2. The pointer i scans the array nums from front to back, and the pointer j points to nums[0]. If nums[i] is not equal to val, it is assigned to nums[j], and j points to the next position. Otherwise, count+1.
  3. Return numsSize-count.

Time complexity: O(n) Space complexity: O(1)

Illustration:

Code:

int removeElement(int* nums, int numsSize, int val){
    int count = 0;
    for (int i = 0, j = 0; i < numsSize; i++)
    {
        if (nums[i] != val)
        {
            nums[j] = nums[i];
            j++;
        }
        else
        {
            count++;
        }
    }

    return numsSize - count;
}

If the content of this article is helpful to you, you can like it and save it. Thank you for your support and look forward to your attention.

Preview of the next article in this column: Single linked list implementation and exercises

Guess you like

Origin blog.csdn.net/2301_79391308/article/details/133235843