[데이터 구조] 순차 목록, 연결 목록


提示:以下是本篇文章正文内容,下面案例可供参考

1. 시퀀스 테이블

시퀀스 테이블은 데이터 요소가 연속적인 물리적 주소를 가진 저장 단위에 순차적으로 저장되는 선형 구조이며 일반적으로 어레이에 저장됩니다. 어레이의 데이터를 추가, 삭제, 확인 및 수정합니다.
정적 시퀀스 테이블(배열)과 동적 시퀀스 테이블(동적 개발)의 두 가지 구현 방법이 있습니다.
배열의 크기가 고정되어 있기 때문에 공간을 낭비하기 쉬우므로 동적 시퀀스 테이블이 더 나은 선택입니다.

인터페이스 구현

SeqList.h

#pragma once

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

typedef int SLDataType;
typedef struct SeqList
{
    
    
	SLDataType* a;
	int size;       // 记录存储多少个有效数据
	int capacity;   // 空间容量大小 
}SL;

void SLPrint(SL* ps);
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLCheckCapacity(SL* ps);

// 尾插尾删
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);

// 头插头删
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);

// 中间插入删除
// 在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x);
// 删除pos位置数据
void SLErase(SL* ps, int pos);

//int SLFind(SL* ps, SLDataType x);

// begin查找x的起始位置
int SLFind(SL* ps, SLDataType x, int begin);

SeqList.c

#include "SeqList.h"

void SLInit(SL* ps)
{
    
    
	assert(ps);

	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

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

	if (ps->size == ps->capacity)
	{
    
    
		int newcapacity = ((ps->capacity == 0) ? 4 : 2 * ps->capacity);
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType) * 2);
		if (tmp==NULL)
		{
    
    
			perror("realloc fail!");
			exit(-1);
		}
		ps->capacity = newcapacity;
		ps->a = tmp;
	}
}

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

	//SLCheckCapacity(ps);
	//ps->a[ps->size] = x;
	//ps->size++;
	SLInsert(ps, ps->size, x);
}

void SLPopBack(SL* ps)
{
    
    
	//assert(ps);
	//assert(ps->size > 0);

	//ps->a[ps->size - 1] = 0;
	//ps->size--;
	SLErase(ps, ps->size - 1);
}

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

	//SLCheckCapacity(ps);
	//int i = 0;
	//for (i = ps->size - 1; i >= 0; i--)
	//{
    
    
	//	ps->a[i+1] = ps->a[i];
	//}
	//ps->a[0] = x;
	//ps->size++;
	SLInsert(ps, 0, x);
}

void SLPopFront(SL* ps)
{
    
    
	//assert(ps);
	//assert(ps->size > 0);

	//int i = 0;
	//for (i = 1; i < ps->size; i++)
	//{
    
    
	//	ps->a[i - 1] = ps->a[i];
	//}
	//ps->size--;

	SLErase(ps, 0);
}

void SLInsert(SL* ps, int pos, SLDataType x)
{
    
    
	assert(ps);
	assert(pos >= 0);
	assert(pos <= ps->size);

	SLCheckCapacity(ps);
	int i = ps->size - 1;
	for (i = ps->size - 1; i >= pos; i--)
	{
    
    
		ps->a[i + 1] = ps->a[i];
	}
	ps->a[pos] = x;
	ps->size++;
}

void SLErase(SL* ps, int pos)
{
    
    
	assert(ps);
	assert(pos >= 0);
	assert(pos < ps->size);

	int i = pos+1;
	for (i = pos+1; i < ps->size; i++)
	{
    
    
		ps->a[i - 1] = ps->a[i];
	}
	ps->size--;
}

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

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

	if (ps->a)
	{
    
    
		free(ps->a);
		ps->a = NULL;
		ps->size = 0;
		ps->capacity = 0;
	}
}

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

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

주제

1. 요소 제거

배열 nums 및 값 val이 주어지면 값이 val과 동일한 모든 요소를 ​​제자리에서 제거하고 제거된 배열의 새 길이를 반환해야 합니다.
추가 배열 공간을 사용하지 마십시오. O(1) 추가 공간만 사용하고 입력 배열을 제자리에서 수정해야 합니다.
요소의 순서를 변경할 수 있습니다. 새 길이를 초과하는 배열의 요소를 고려할 필요가 없습니다.

int removeElement(int* nums, int numsSize, int val){
    
    
    int right=0;
    int left=0;
    for(right=0;right<numsSize;right++)
    {
    
    
        if(nums[right]!=val)
        {
    
    
            nums[left]=nums[right];
            left++;
        }
    }
    return left;
}

2. 정렬된 배열에서 중복 제거

배열 번호가 오름차순으로 주어지면 각 요소가 한 번만 나타나도록 제자리에서 반복되는 요소를 삭제하고 삭제된 배열의 새 길이를 반환하십시오. 요소의 상대적 순서는 일관되어야 합니다.
일부 언어에서는 배열의 길이를 변경할 수 없으므로 결과는 배열 nums의 첫 번째 부분에 배치해야 합니다. 더 정식으로, 중복을 제거한 후 k 요소가 있는 경우 nums의 첫 번째 k 요소가 최종 결과를 보유해야 합니다.
nums의 처음 k 위치에 최종 결과를 삽입한 후 k를 반환합니다.
추가 공간을 사용하는 대신 입력 배열을 제자리에서 수정해야 하며 O(1) 추가 공간을 사용하여 수정해야 합니다.

int removeDuplicates(int* nums, int numsSize){
    
    
    int fast=0;
    int slow=0;
     while(fast<numsSize)
     {
    
    
       if(nums[fast]==nums[slow])
       {
    
    
           fast++;
       }  
       else
       {
    
    
           slow++;
           nums[slow]=nums[fast];
           fast++;
       }
     }
     return slow+1;
}

둘, 연결된 목록

연결 리스트는 물리적 저장 구조에서 비연속적이고 비순차적인 저장 구조로, 데이터 요소의 논리적 순서는 연결 리스트에서 포인터의 연결 순서를 통해 구현된다.

인터페이스 구현

1. 단일 연결 리스트

여기에 이미지 설명 삽입

슬리스트.h

#pragma once

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

typedef int SLTDateType;
typedef struct SListNode
{
    
    
	SLTDateType data;
	struct SListNode* next;
}SListNode;
 
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);

// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x);
//在pos之前插入值
void SListInsert(SListNode** pphead, SListNode* pos, SLTDateType x);

// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);
//删除pos位置的值
void SListErase(SListNode** pphead, SListNode* pos);
// 单链表的销毁
void SListDestroy(SListNode** plist);

SListNode* CreateSList(int n);

슬리스트.c

#include "Slist.h"

SListNode* BuySListNode(SLTDateType x)
{
    
    
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

SListNode* CreateSList(int n)
{
    
    
	SListNode* phead = NULL, * ptail = NULL;
	int i = 0;
	for (i = 0; i < n; i++)
	{
    
    
		SListNode* newnode = BuySListNode(i);
		if (phead == NULL)
		{
    
    
			phead = ptail = newnode;
		}
		else
		{
    
    
			ptail->next = newnode;
			ptail = newnode;
		}
	}
	return phead;
}

void SListPrint(SListNode* plist)
{
    
    
	SListNode* cur = plist;
	while (cur != NULL)
	{
    
    
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("NULL");
}

void SListPushBack(SListNode** pplist, SLTDateType x)
{
    
    
	SListNode* newnode = BuySListNode(x);
	if (*pplist == NULL)
	{
    
    
		*pplist = newnode;
	}
	else
	{
    
    
		SListNode* tail = *pplist;
		while (tail->next)
		{
    
    
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

void SListPopBack(SListNode** pplist)
{
    
    
	assert(*pplist);
	if ((*pplist)->next == NULL)
	{
    
    
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
    
    
		SListNode* prev = NULL;
		SListNode* tail = *pplist;
		while (tail->next)
		{
    
    
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}
}

void SListPushFront(SListNode** pplist, SLTDateType x)
{
    
    
	SListNode* newnode = BuySListNode(x);
	newnode->next = *pplist;
	*pplist = newnode;
}

void SListPopFront(SListNode** pplist)
{
    
    
	assert(*pplist);
	SListNode* next = *pplist;
	free(*pplist);
	*pplist = next;
}

SListNode* SListFind(SListNode* plist, SLTDateType x)
{
    
    
	assert(plist);
	SListNode* cur = plist;
	while (cur)
	{
    
    
		if (cur->data == x)
		{
    
    
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

//在pos后插入数据
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
    
    
	assert(pos);

	SListNode* newnode = BuySListNode(x);
	SListNode* next = pos->next;
	pos->next = newnode;
	newnode->next = next;
}

//在pos之前插入值
void SListInsert(SListNode** pphead, SListNode* pos, SLTDateType x)
{
    
    
	assert(pos);

	if (*pphead == pos)
	{
    
    
		SListPushFront(pphead, x);
	}
	else
	{
    
    
		SListNode* newnode = BuySListNode(x);
		SListNode* prev = *pphead;
		while (prev->next != pos)
		{
    
    
			prev = prev->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}
}

void SListEraseAfter(SListNode* pos)
{
    
    
	assert(pos);

	if (pos->next == NULL)
	{
    
    
		return;
	}
	else
	{
    
    
		SListNode* nextnode = pos->next;
		pos->next = nextnode->next;
		free(nextnode);
	}
}

//删除pos位置的值
void SListErase(SListNode** pphead, SListNode* pos)
{
    
    
	assert(pos);
	assert(*pphead);

	if (pos == *pphead)
	{
    
    
		SListPopFront(pphead);
	}
	else
	{
    
    
		SListNode* prev = *pphead;
		while (prev->next != pos)
		{
    
    
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
}

void SListDestroy(SListNode** pplist)
{
    
    
	SListNode* cur = *pplist;
	while (cur)
	{
    
    
		SListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pplist = NULL;
}

2. 센티넬 비트가 있는 이중 연결 리스트

여기에 이미지 설명 삽입

List.h

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>

typedef int LTDataType;

typedef struct ListNode
{
    
    
	struct ListNode* next;
	struct ListNode* prev;
	LTDataType data;
}LTNode;

LTNode* BuyListNode(LTDataType x);
LTNode* LTInit();

void LTPrint(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);

void LTPushFront(LTNode* phead, LTDataType x);
void LTPopFront(LTNode* phead);

LTNode* LTFind(LTNode* phead, LTDataType x);

//在pos之前插入
void LTInsert(LTNode* pos, LTDataType x);

void LTErase(LTNode* pos);

bool LTEmpty(LTNode* phead);
size_t LTSize(LTNode* phead);
void LTDestroy(LTNode* phead);

List.c

#include "List.h"

LTNode* BuyListNode(LTDataType x)
{
    
    
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	node->data = x;
	node->next = NULL;
	node->prev = NULL;

	return node;
}

LTNode* LTInit()
{
    
    
	LTNode* phead = BuyListNode(-1);
	phead->next = phead;
	phead->prev = phead;

	return phead;
}

void LTPrint(LTNode* phead)
{
    
    
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

void LTPushBack(LTNode* phead, LTDataType x)
{
    
    
	assert(phead);

	/*LTNode* newnode = BuyListNode(x);
	LTNode* prev = phead->prev;

	phead->prev = newnode;
	newnode->next = phead;
	newnode->prev = prev;
	prev->next = newnode;*/

	LTInsert(phead, x);
}

void LTPopBack(LTNode* phead)
{
    
    
	assert(phead);
	assert(phead->next != phead);

	/*LTNode* tail = phead->prev;
	phead->prev = tail->prev;
	tail->prev->next = phead;
	free(tail);*/

	LTErase(phead->prev);
}

void LTPushFront(LTNode* phead, LTDataType x)
{
    
    
	assert(phead);
	/*LTNode* newnode = BuyListNode(x);

	LTNode* head = phead->next;
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = head;
	head->prev = newnode;*/
	LTInsert(phead->next, x);
}

void LTPopFront(LTNode* phead)
{
    
    
	assert(phead);
	assert(phead->next != phead);

	/*LTNode* first = phead->next;
	LTNode* second = first->next;
	phead->next = second;
	second->prev = phead;
	free(first);*/
	LTErase(phead->next);
}

LTNode* LTFind(LTNode* phead, LTDataType x)
{
    
    
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		if (cur->data == x)
		{
    
    
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

//在pos之前插入
void LTInsert(LTNode* pos, LTDataType x)
{
    
    
	assert(pos);

	LTNode* newnode = BuyListNode(x);
	LTNode* prev = pos->prev;
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

//删除pos位置的值
void LTErase(LTNode* pos)
{
    
    
	assert(pos);

	LTNode* prev = pos->prev;
	LTNode* next = pos->next;
	prev->next = next;
	next->prev = prev;
	free(pos);
}

bool LTEmpty(LTNode* phead)
{
    
    
	assert(phead);
	return phead->next == phead;
}

size_t LTSize(LTNode* phead)
{
    
    
	assert(phead);
	size_t size = 0;
	LTNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		size++;
		cur = cur->next;
	}
	return size;
}

void LTDestroy(LTNode* phead)
{
    
    
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

주제

다음은 보다 포괄적인 질문입니다.

임의의 포인터로 연결된 목록 복사

길이가 n인 연결 목록이 주어지면 각 노드에는 연결된 목록의 모든 노드 또는 빈 노드를 가리킬 수 있는 추가 임의 포인터 random이 포함됩니다.
이 연결된 목록의 전체 복사본을 구성합니다. 깊은 복사는 정확히 n개의 완전히 새로운 노드로 구성되어야 하며, 각 새 노드의 값은 해당하는 원래 노드의 값으로 설정됩니다. 새 노드의 다음 포인터와 임의 포인터도 복사된 연결 목록의 새 노드를 가리켜야 원본 연결 목록과 복사된 연결 목록의 포인터가 동일한 연결 목록 상태를 나타낼 수 있습니다. 복사된 연결 목록의 포인터는 원래 연결 목록의 노드를 가리키면 안 됩니다.
예를 들어 원래 연결 목록에 두 개의 노드 X와 Y가 있는 경우 X.random --> Y입니다. 그런 다음 복사 연결 목록의 해당 두 노드 x 및 y도 x.random --> y를 갖습니다.
복사된 목록의 헤드 노드를 반환합니다.
입력/출력의 연결 목록은 n 노드의 연결 목록으로 표시됩니다. 각 노드는 [val, random_index]로 표현된다:
val: Node.val을 나타내는 정수.
random_index: 임의 포인터가 가리키는 노드의 인덱스(범위는 0에서 n-1까지), 어떤 노드도 가리키지 않으면 null입니다.
귀하의 코드는 들어오는 매개 변수로 원래 연결된 목록의 헤드 노드 헤드만 허용합니다.

내 아이디어 제공:

  1. 각 노드 뒤에 복사 노드를 삽입하여 이전 노드와 동일한 값을 저장하는 것이 좋습니다.
  2. 복사 노드에서 무작위로 가리키는 노드는 소스 노드의 무작위 노드 다음 노드입니다.
  3. 연결 리스트를 다시 만들고 위에서 복사한 노드를 새로운 연결 리스트에 계속해서 삽입합니다.
struct Node* copyRandomList(struct Node* head) {
    
    
	struct Node* cur=head;
	//复制节点
    while(cur)
    {
    
    
        struct Node* copy=(struct Node*)malloc(sizeof(struct Node));
        struct Node* next=cur->next;
        copy->val=cur->val;
        cur->next=copy;
        copy->next=next;
        cur=next;
    }
    cur=head;
    //复制random
    while(cur)
    {
    
    
        struct Node* copy=cur->next;
        if(cur->random==NULL)
        {
    
    
            copy->random=NULL;
        }
        else
        {
    
    
            copy->random=cur->random->next;
        }
        cur=cur->next->next;
    }

     struct Node* copyhead=NULL,*copytail=NULL;
     cur=head;
     //复制节点单独拿出来
     while(cur)
     {
    
    
         struct Node* copy=cur->next;
         struct Node* next=copy->next;
         cur->next=next;
         if(copyhead==NULL)
         {
    
    
             copyhead=copytail=copy;
         }
         else
         {
    
    
             copytail->next=copy;
             copytail=copytail->next;
         }
         
         cur=next;
     }

     return copyhead;
}

추천

출처blog.csdn.net/lyq2632750277/article/details/128433707