導入
シーケンシャル リストとリンク リストは、データ構造における一般的な線形リストです。それらの主な違いは、メモリの管理方法です。
シーケンス テーブル (配列) は、一定の順序で配置された一連の要素で構成され、連続したメモリ空間を使用してデータを格納します。シーケンシャル テーブルは配列を使用してデータを保存し、配列内の各要素には添字を介してアクセスできます。シーケンシャル テーブルには、ランダム アクセスと高いスペース使用率という利点がありますが、要素を挿入および削除するときに他の要素を移動する必要があるため、効率が低くなります。シーケンシャル テーブルは、要素を頻繁に変更する必要がある場合の使用には適していません。
リンク リスト (List) は、ポインターによって実装された動的データ構造であり、あらゆる種類のデータを順不同で格納できます。リンクされたリスト内の要素はノードと呼ばれ、各ノードにはデータと次のノードへのポインタが含まれます。対照的に、リンク リストのメモリ割り当ては動的であり、事前に配列サイズを計画する必要がないため、メモリの無駄を効果的に回避できます。リンク リストではノードの挿入と削除を迅速に行うことができますが、挿入と削除は最初に指定された場所に配置する必要があるため、アクセス時間はシーケンシャル リストよりも長くなります。
つまり、シーケンスを頻繁にクエリし、内部要素に頻繁にアクセスし、事前定義された固定サイズでデータを保存する必要がある場合は、シーケンス テーブルを使用する方が適切な選択となる可能性があります。ただし、多数の要素を挿入、削除、変更する必要があり、スペース サイズを事前に予測できない場合は、リンク リストの動的割り当て方法がより効果的です。
シーケンステーブルの反転を実装する
/***************************************
* 函数功能:逆置顺序表
* 函数参数:int*data-表示待逆置的顺序表 int size-表示顺序表的大小
* 函数返回值:无
****************************************/
void reverse(int*data, int size)
{
for (int i = 0; i < size / 2; ++i)
{
int temp = data[i];
data[i] = data[size - i - 1];
data[size - i - 1] = temp;
}
}
関数本体では for ループが発生し、ループ数はデータ サイズ N に線形の関係があります。さらに、一定数の代入操作が for ループ内で実行されます。したがって、関数全体の時間計算量は O(N) になります。(N はシーケンス リストの長さを表します)
この関数の空間計算量は O(1) (一定レベル) です。関数の実行中、一定数の変数 (i、temp) のみが使用され、問題サイズ N が増加しても使用されるスペースは増加しないため、関数のスペース複雑さは O(1) です。
したがって、この関数の時間計算量は O(N)、空間計算量は O(1) になります。
リンクリストの反転を実装する
コアコード
/***************************************
* 函数功能:逆置链表
* 函数参数:struct myHead* list待逆置链表
* 函数返回值:无
****************************************/
void reverseList(struct myHead* list)
{
struct myNode*p = list->element->next,*q = NULL;
// 遍历链表
while (p)
{
// 保存p的后继节点
struct myNode*temp = p->next;
// 改变指针指向的位置
p->next = q;
q = p;
p = temp;
}
// 保存反转链表的结果
list->element->next = q;
}
アルゴリズムの核となる設計思想は、リンクされたリスト内のノード ポインターが指す位置を変更すること、つまり、先行要素と後続要素の位置を変更することです。
リンク リストの要素が 1、2、3、4、5 の場合、反転プロセスの図は次のようになります。
この関数には主に、リンク リストの走査とリンク リストの逆方向の 2 つの操作があります。リンク リストをトラバースするプロセス中に、各ノードとノードに格納されているデータにアクセスして操作する必要があります。したがって、関数の時間計算量は O(N) です。(N はリンク リストの長さを表します)
関数の空間計算量は O(1) です。この関数は、いくつかの一時変数を格納するために定数レベルの追加スペースのみを使用し、これらの変数が占めるスペースは問題のサイズ N とは関係がないため、関数のスペース複雑さは O(1) であると考えることができます。
リンクされたリストに付加されたいくつかの中間関数
リンクリスト構造
struct myNode
{
int data;
struct myNode*next;
};
struct myHead
{
int size;
struct myNode*element;
};
初期化、ノードの追加、リンク リストの出力など、リンク リストのいくつかの基本操作。
/***************************************
* 函数功能:链表初始化
* 函数参数:无
* 函数返回值:无
****************************************/
struct myHead*listInit(void)
{
// 初始化头结点
struct myHead*myList = (struct myHead*)malloc(sizeof(struct myHead));
myList->size = 0;
// 添加虚拟节点
myList->element = (struct myNode*)malloc(sizeof(struct myNode));
myList->element->data = -1;
myList->element->next = NULL;
return myList;
}
/***************************************
* 函数功能:使用尾插法插入值至链表中
* 函数参数:struct myHead*list表示目标链表 int data表示待插入的值
* 函数返回值:无
****************************************/
void addNode(struct myHead*list, int data)
{
struct myNode*p = list->element;
// 遍历至待插入节点的前驱节点
while (p->next)
p = p->next;
// 新建节点
struct myNode*myNode = (struct myNode*)malloc(sizeof(struct myNode));
myNode->data = data;
myNode->next = NULL;
// 插入节点并增加链表节点数量
p->next = myNode;
list->size++;
}
/***************************************
* 函数功能:输出链表
* 函数参数:struct myHead* list待输出链表
* 函数返回值:无
****************************************/
void outPutList(struct myHead* list)
{
struct myNode *p = list->element->next;
while (p)
{
printf("%d ",p->data);
p = p->next;
}
}