この質問は、王道の単著のデータ構造のリンクリストにある授業後の質問の最初の質問で、最初は長い間考えていましたが、理解できませんでした。できませんでした。クラスを読んだ後、答えがわかりませんでした。久しぶりに解決しました。このリンクリストアルゴリズムの問題の核となるアイデアは次のとおりです。
タイトルの要件は次のとおりです。
- 再帰
- リードノードなし
単一リンクリスト内の値がxであるすべてのノードを削除します。通常の線形テーブル削除の場合は、誰でも実行できると思いますが、先頭ノードなしで再帰的に削除する方が頭が痛いです。
最近は比較的時間が短いので、絵を描いて自分の考えを描くには遅すぎます。答えを直接調べてみましょう!
私が書いたコードについて(コメントに注意してください):
/*
1.设计一个递归算法,删除不带头结点的单链表 L 中所有值为 x 的结点
*/
void RecursionDeleteX(LinkList &L, Element x){
if(L == NULL) //若 L 为空表,则直接进行返回
return;
if(L->data == x){
//若 L->data 为 x, 则对其进行删除
LNode *p = L; //定义指针变量 p 指向当前结点
/*
非常重要!!!
该结点指针 L 后移,指向下一结点,注意,由于 L指针为引用数据类型,
所以L指向结点变化时,它上一层结点的next域也将指向下一结点,这样就可以避免删除操作造成的断链现象
*/
L = L->next;
free(p); //此时再将该结点删除
RecursionDeleteX(L, x); //继续递归查找删除子链表中值为 x 的结点
}else{
RecursionDeleteX(L->next, x); //结点不是x的话就继续递归下层子链表
}
}
-
最初のステップは再帰プログラムの終了です。この時点で現在のノードLがNULLの場合、単一リンクリスト内のすべてのノードがトラバースされ、値xのすべてのノードの削除操作が完了したことを意味します。呼び出しが返されます。
-
2番目のステップでは、ノードがNULLでなく、ノードの値がxの場合、ノードを削除する必要があることを意味します。
手順を削除する:(1)削除の準備ができているノードを指す一時的なポインター変数pを定義します。
(2)。L = L-> next;このステップは非常に重要です。このステップを理解すると、Lポインターを後方に移動して次のノードを指すようにするという、この質問の本質を理解できます。 、リンクリストは切断されますこのノードの接続は、後続のノードを再帰的にトラバースし続ける準備をします。
このとき、友達の中には疑問があるはずです。L= L-> next、元のLノードが失われるのではないか、チェーンが壊れているのですが、リンクリストでLの後に後継ノードを見つけるにはどうすればよいですか、私も行き詰まりました実際、この操作によってチェーンが切断されることはありません。
その理由は、渡すパラメーターLが参照データ型であり、パラメーターの変更が元の変数の変更であるためです。つまり、このレイヤーのLの変更は自動的にL->に同期されます。前のレイヤーの次へ移動します。つまり、前のレイヤーのL-> nextは、L-> next-> nextも指します。これにより、リンクリストが継続的にチェーンされます。
(3)free関数を使用してノードを解放します。
(4)後続ノードをトラバースするために、関数を再帰的に呼び出し続けます。
-
3番目のステップでは、ノードがNULLでなく、ノードの値がxでない場合、関数を再帰的に呼び出して、後続のノードをトラバースし続けます。
上記の3つの手順により、再帰メソッドを使用して、単一リンクリストの特定の値を削除できます。!
実行中のコードのスクリーンショット(データ「5」を削除):
さて、これはこの質問の私の理解です。私はもともと質問6の単一リンクリストを並べ替え、質問8の単一リンクリストのパブリックノードを書きたかったのですが、時間が足りないので、以下のコードを貼り付けてください。興味のある方は是非ご覧ください!
/*
6.设计一个带头结点的单链表 L , 设计一个算法使其元素递增有序
本题算法思想,将单链表 L 从头部拆分为两个链表,第一个链表初始化仅有一个元素,
故有序,逐渐将第二个链表中的元素插入到第一个链表 L , 使其依旧有序,当全部插入完成后,该链表就实现了排序
类似于 H---->12---->2---->1----->5---->3......
L1 = (H---->12---->NULL) //pre指针作为循环遍历该链表的指针, 用于确定插入位置
L2 = (2---->1---->5---->3......) //逐渐将L2的结点按序插入L1即可完成排序, 其中p指向该链表的表头结点, r为p后继结点
*/
void SortLinkList(LinkList &L){
if(L->next == NULL)
return;
LNode *p, *pre, *r; //p 作为 循环遍历链表 L2, pre 作为 循环遍历 L1, r暂存p的后继结点
p = L->next; //实现链表的分割
r = p->next;
p->next = NULL;
p = r;
while(p != NULL){
//循环遍历第二个链表,将其有序插入链表1中
r = p->next;
pre = L;
while(pre->next!=NULL && pre->next->data < p->data) //确定插入位置
pre = pre->next;
p->next = pre->next; //进行插入操作
pre->next = p;
p = r; // p 指针后移
}
}
/*
8.给定两个单链表,编写算法找到两个结点的公共结点
*/
LinkList GetPublicNode(LinkList &L1, LinkList &L2){
if(L1->next==NULL || L2->next==NULL)
return false;
int len1 = Length(L1), len2 = Length(L2), dist;
LinkList short_list, long_list;
if(len1 > len2){
//确定一个较长的链表和较短的链表,并得到他们的差值
long_list = L1->next;
short_list = L2->next;
dist = len1-len2;
}else{
long_list = L2->next;
short_list = L1->next;
dist = len2-len1;
}
for(int i=1; i<=dist; i++){
//根据差值,长链表指针依次后移,目的实现长短链表同步开始寻找公共结点
long_list = long_list->next;
}
while(long_list != NULL){
//循环遍历寻找公共结点
if(short_list->data == long_list->data)
return long_list;
short_list = short_list->next;
long_list = long_list->next;
}
return NULL;
}