คำถามนี้อาจจะเก่า แต่ฉันคิดคำตอบไม่ออก
บอกว่ามีสองรายการของความยาวที่แตกต่างกัน, การควบรวมที่จุด ; เราจะรู้ได้อย่างไรว่าจุดรวมอยู่ที่ไหน?
เงื่อนไข:
- เราไม่รู้ความยาว
- เราควรแยกวิเคราะห์แต่ละรายการเพียงครั้งเดียว
คำถามนี้อาจจะเก่า แต่ฉันคิดคำตอบไม่ออก
บอกว่ามีสองรายการของความยาวที่แตกต่างกัน, การควบรวมที่จุด ; เราจะรู้ได้อย่างไรว่าจุดรวมอยู่ที่ไหน?
เงื่อนไข:
คำตอบ:
ถ้า
อัลกอริทึมต่อไปนี้จะเป็นทางออก
อันดับแรกคือตัวเลข สมมติว่ารายการแรกมีความยาวa+c
และรายการที่สองมีความยาวb+c
โดยที่c
ความยาวของ "หาง" ทั่วไปของพวกเขาอยู่ที่ไหน(หลังจุดผสาน) ขอแสดงความหมายดังนี้:
x = a+c
y = b+c
เนื่องจากเราไม่ทราบความยาวเราจึงคำนวณx
และy
ไม่ต้องทำซ้ำเพิ่มเติม คุณจะเห็นว่า
จากนั้นเราจะวนซ้ำแต่ละรายการและย้อนกลับในขณะที่ทำซ้ำ! หากตัววนซ้ำทั้งสองไปถึงจุดผสานในเวลาเดียวกันเราจะพบว่ามันเป็นเพียงการเปรียบเทียบ มิฉะนั้นตัวชี้ตัวหนึ่งจะไปถึงจุดผสานก่อนอีกตัวหนึ่ง
หลังจากนั้นเมื่อตัววนซ้ำอีกตัวไปถึงจุดผสานมันจะไม่ไปที่หางทั่วไป แต่จะกลับไปที่จุดเริ่มต้นเดิมของรายการที่เคยถึงจุดรวมมาก่อน! ดังนั้นก่อนที่จะถึงจุดสิ้นสุดของรายการที่เปลี่ยนแปลง (เช่นจุดเริ่มต้นเดิมของรายการอื่น) เขาจะทำการa+b+1
วนซ้ำทั้งหมด ขอเรียกว่าz+1
.
ตัวชี้ที่มาถึงจุดผสานก่อนจะทำซ้ำไปเรื่อย ๆ จนกว่าจะถึงจุดสิ้นสุดของรายการ x
จำนวนการทำซ้ำที่ทำควรจะคำนวณและจะมีค่าเท่ากับ
จากนั้นตัวชี้นี้จะวนกลับและย้อนกลับรายการอีกครั้ง แต่ตอนนี้มันจะไม่กลับไปที่จุดเริ่มต้นของรายการที่เริ่มต้นจากเดิม! แต่มันจะไปที่จุดเริ่มต้นของรายการอื่น ๆ แทน! จำนวนการทำซ้ำควรคำนวณและเท่ากับy
จำนวนซ้ำมันทำให้ควรจะคำนวณและเท่ากับ
ดังนั้นเราจึงทราบตัวเลขต่อไปนี้:
x = a+c
y = b+c
z = a+b
จากที่เรากำหนดว่า
a = (+x-y+z)/2
b = (-x+y+z)/2
c = (+x+y-z)/2
ซึ่งช่วยแก้ปัญหาได้
ต่อไปนี้เป็นสิ่งที่ยิ่งใหญ่ที่สุดที่ฉันเคยเห็น - O (N) ไม่มีเคาน์เตอร์ ผมได้รับมันในระหว่างการสัมภาษณ์กับผู้สมัครที่ SN VisionMap
สร้างตัวชี้จำนวนเต็มเช่นนี้มันจะไปข้างหน้าทุกครั้งจนถึงจุดสิ้นสุดจากนั้นข้ามไปที่จุดเริ่มต้นของรายการตรงข้ามและอื่น ๆ สร้างสองสิ่งนี้โดยชี้ไปที่สองหัว เลื่อนพอยน์เตอร์ทีละ 1 ตัวทุกครั้งจนกว่าจะพบ สิ่งนี้จะเกิดขึ้นหลังจากผ่านไปหนึ่งหรือสองครั้ง
ฉันยังคงใช้คำถามนี้ในการสัมภาษณ์ แต่เพื่อดูว่าต้องใช้เวลานานแค่ไหนในการทำความเข้าใจว่าเหตุใดวิธีนี้จึงได้ผล
a-b-c-x-y-z
และp-q-x-y-z
. เส้นทางของตัวชี้แรกa,b,c,x,y,z,p,q,x
เส้นทางของตัวชี้ที่สองp,q,x,y,z,a,b,c,x
คำตอบของ Pavel ต้องการการแก้ไขรายการและการทำซ้ำแต่ละรายการสองครั้ง
นี่คือวิธีแก้ปัญหาที่ เพียง แต่ต้องมีการทำซ้ำแต่ละรายการสองครั้ง (ครั้งแรกในการคำนวณความยาวของพวกเขาถ้ายาวจะได้รับคุณต้องการเพียงเพื่อย้ำครั้งเดียว)
แนวคิดคือการละเว้นรายการเริ่มต้นของรายการที่ยาวขึ้น (จุดผสานไม่สามารถอยู่ที่นั่นได้) เพื่อให้พอยน์เตอร์ทั้งสองอยู่ห่างจากจุดสิ้นสุดของรายการเท่ากัน จากนั้นย้ายไปข้างหน้าจนกว่าจะรวมกัน
lenA = count(listA) //iterates list A
lenB = count(listB) //iterates list B
ptrA = listA
ptrB = listB
//now we adjust either ptrA or ptrB so that they are equally far from the end
while(lenA > lenB):
ptrA = ptrA->next
lenA--
while(lenB > lenA):
prtB = ptrB->next
lenB--
while(ptrA != NULL):
if (ptrA == ptrB):
return ptrA //found merge point
ptrA = ptrA->next
ptrB = ptrB->next
นี่เหมือนกับ (เวลาเชิงเส้น) แบบไม่มีอาการกับคำตอบอื่น ๆ ของฉัน แต่อาจมีค่าคงที่น้อยกว่าจึงน่าจะเร็วกว่า แต่ฉันคิดว่าคำตอบอื่นของฉันเจ๋งกว่า
ถ้าคุณรู้ว่าพวกเขาจะรวมเข้าด้วยกัน:
สมมติว่าคุณเริ่มต้นด้วย:
A-->B-->C
|
V
1-->2-->3-->4-->5
1) ผ่านรายการแรกที่ตั้งค่าตัวชี้แต่ละตัวถัดไปเป็น NULL
ตอนนี้คุณมี:
A B C
1-->2-->3 4 5
2) ไปที่รายการที่สองและรอจนกว่าคุณจะเห็น NULL นั่นคือจุดผสานของคุณ
หากคุณไม่สามารถแน่ใจได้ว่าพวกมันผสานคุณสามารถใช้ค่า Sentinel สำหรับค่าตัวชี้ได้ แต่ก็ไม่ได้สวยหรู
หากเราสามารถทำซ้ำรายการได้สองครั้งมากกว่าที่ฉันสามารถระบุวิธีการกำหนดจุดผสาน:
นี่เป็นวิธีแก้ปัญหาอย่างรวดเร็วในการคำนวณ (วนซ้ำแต่ละรายการครั้งเดียว) แต่ใช้หน่วยความจำมาก:
for each item in list a
push pointer to item onto stack_a
for each item in list b
push pointer to item onto stack_b
while (stack_a top == stack_b top) // where top is the item to be popped next
pop stack_a
pop stack_b
// values at the top of each stack are the items prior to the merged item
คุณสามารถใช้ชุดของโหนด วนซ้ำในรายการเดียวและเพิ่มแต่ละโหนดในชุด จากนั้นวนซ้ำในรายการที่สองและสำหรับการวนซ้ำทุกครั้งให้ตรวจสอบว่ามีโหนดอยู่ในชุดหรือไม่ ถ้าเป็นเช่นนั้นคุณจะพบจุดผสานของคุณ :)
เนื้อหานี้ละเมิดเงื่อนไข "แยกวิเคราะห์แต่ละรายการเพียงครั้งเดียว" แต่ใช้อัลกอริทึมเต่าและกระต่าย (ใช้เพื่อค้นหาจุดผสานและความยาวรอบของรายการแบบวนรอบ) ดังนั้นคุณจึงเริ่มต้นที่รายการ A และเมื่อคุณไปถึง NULL ที่ สิ้นสุดที่คุณแสร้งทำเป็นตัวชี้ไปที่จุดเริ่มต้นของรายการ B ดังนั้นการสร้างลักษณะของรายการแบบวนรอบ จากนั้นอัลกอริทึมจะบอกให้คุณทราบว่า List A การผสานอยู่ไกลแค่ไหน (ตัวแปร 'mu' ตามคำอธิบายของ Wikipedia)
นอกจากนี้ค่า "lambda" จะบอกความยาวของรายการ B และหากคุณต้องการคุณสามารถคำนวณความยาวของรายการ A ระหว่างอัลกอริทึม (เมื่อคุณเปลี่ยนเส้นทางลิงก์ NULL)
บางทีฉันอาจจะง่ายกว่านี้ แต่เพียงแค่วนซ้ำรายการที่เล็กที่สุดและใช้โหนดสุดท้ายLink
เป็นจุดรวม?
ดังนั้นData->Link->Link == NULL
จุดสิ้นสุดอยู่ที่ไหนโดยให้Data->Link
เป็นจุดรวม (ที่ส่วนท้ายของรายการ)
แก้ไข:
เอาล่ะจากภาพที่คุณโพสต์คุณจะแยกวิเคราะห์สองรายการที่เล็กที่สุดก่อน ด้วยรายการที่เล็กที่สุดคุณสามารถรักษาการอ้างอิงไปยังโหนดต่อไปนี้ ตอนนี้เมื่อคุณแยกวิเคราะห์รายการที่สองคุณจะทำการเปรียบเทียบกับข้อมูลอ้างอิงเพื่อค้นหาว่า Reference [i] คือการอ้างอิงที่ LinkedList [i] -> Link สิ่งนี้จะทำให้จุดผสาน เวลาอธิบายด้วยรูปภาพ (ซ้อนค่าบนภาพ OP)
คุณมีรายการที่เชื่อมโยง (ข้อมูลอ้างอิงที่แสดงด้านล่าง):
A->B->C->D->E
คุณมีรายการที่สองที่เชื่อมโยง:
1->2->
ด้วยรายการที่ผสานการอ้างอิงจะเป็นดังนี้:
1->2->D->E->
ดังนั้นคุณจึงจับคู่รายการที่ "เล็กกว่า" รายการแรก (เนื่องจากรายการที่ผสานซึ่งเป็นสิ่งที่เรากำลังนับมีความยาว 4 และรายการหลัก 5)
วนซ้ำรายการแรกรักษาข้อมูลอ้างอิงของการอ้างอิง
Pointers { 1, 2, D, E }
รายการนี้จะมีการอ้างอิงต่อไป
ตอนนี้เราไปถึงรายการที่สอง:
-> A - Contains reference in Pointers? No, move on
-> B - Contains reference in Pointers? No, move on
-> C - Contains reference in Pointers? No, move on
-> D - Contains reference in Pointers? Yes, merge point found, break.
แน่นอนว่าคุณมีรายการตัวชี้ใหม่ แต่ไม่ได้อยู่นอกข้อกำหนด อย่างไรก็ตามรายการแรกจะถูกแยกวิเคราะห์เพียงครั้งเดียวและรายการที่สองจะถูกแยกวิเคราะห์อย่างสมบูรณ์ก็ต่อเมื่อไม่มีจุดผสาน มิฉะนั้นจะสิ้นสุดเร็วกว่า (ที่จุดผสาน)
ฉันได้ทดสอบกรณีการผสานบน FC9 x86_64 ของฉันแล้วและพิมพ์ที่อยู่โหนดทั้งหมดตามที่แสดงด้านล่าง:
Head A 0x7fffb2f3c4b0
0x214f010
0x214f030
0x214f050
0x214f070
0x214f090
0x214f0f0
0x214f110
0x214f130
0x214f150
0x214f170
Head B 0x7fffb2f3c4a0
0x214f0b0
0x214f0d0
0x214f0f0
0x214f110
0x214f130
0x214f150
0x214f170
หมายเหตุเนื่องจากฉันได้จัดแนวโครงสร้างโหนดดังนั้นเมื่อ malloc () โหนดแอดเดรสจะถูกจัดแนวด้วย 16 ไบต์โปรดดูอย่างน้อย 4 บิต บิตน้อยที่สุดคือ 0s คือ 0x0 หรือ 000b ดังนั้นหากคุณอยู่ในกรณีพิเศษเดียวกัน (ที่อยู่โหนดที่จัดตำแหน่ง) ด้วยคุณสามารถใช้ 4 บิตเหล่านี้เป็นอย่างน้อย ตัวอย่างเช่นเมื่อเดินทางทั้งสองรายการจากหัวไปหางตั้งค่า 1 หรือ 2 จาก 4 บิตของแอดเดรสโหนดการเยี่ยมชมนั่นคือตั้งค่าสถานะ
next_node = node->next;
node = (struct node*)((unsigned long)node | 0x1UL);
หมายเหตุด้านบนแฟล็กจะไม่ส่งผลกระทบต่อที่อยู่โหนดจริง แต่เฉพาะค่าตัวชี้โหนดที่บันทึกไว้ของคุณ
เมื่อพบว่ามีคนตั้งค่าแฟล็กบิตแล้วโหนดแรกที่พบควรเป็นจุดผสาน หลังจากเสร็จสิ้นคุณจะกู้คืนที่อยู่โหนดโดยล้างแฟล็กบิตที่คุณตั้งไว้ ในขณะที่สิ่งสำคัญคือคุณควรระมัดระวังในการทำซ้ำ (เช่น node = node-> next) เพื่อทำความสะอาด จำไว้ว่าคุณตั้งค่าแฟล็กบิตแล้วให้ทำแบบนี้
real_node = (struct node*)((unsigned long)node) & ~0x1UL);
real_node = real_node->next;
node = real_node;
เนื่องจากข้อเสนอนี้จะคืนค่าที่อยู่โหนดที่แก้ไขจึงถือได้ว่า "ไม่มีการแก้ไข"
อาจมีวิธีง่ายๆ แต่จะต้องมีช่องว่างเสริม แนวคิดคือการสำรวจรายการและจัดเก็บที่อยู่แต่ละรายการในแผนที่แฮชตอนนี้สำรวจรายการอื่นและจับคู่ว่าที่อยู่นั้นอยู่ในแผนที่แฮชหรือไม่ แต่ละรายการจะถูกส่งผ่านเพียงครั้งเดียว ไม่มีการแก้ไขรายการใด ๆ ยังไม่ทราบความยาว ช่องว่างเสริมที่ใช้: O (n) โดยที่ 'n' คือความยาวของรายการแรกที่ข้ามผ่าน
โซลูชันนี้วนซ้ำแต่ละรายการเพียงครั้งเดียว ... ไม่จำเป็นต้องปรับเปลี่ยนรายการด้วย. แม้ว่าคุณอาจบ่นเกี่ยวกับช่องว่าง ..
1) โดยทั่วไปคุณทำซ้ำใน list1 และจัดเก็บที่อยู่ของแต่ละโหนดในอาร์เรย์ (ซึ่งเก็บค่า int ที่ไม่ได้ลงชื่อ)
2) จากนั้นคุณวนซ้ำ list2 และสำหรับที่อยู่ของแต่ละโหนด ---> คุณค้นหาอาร์เรย์ที่คุณพบว่าตรงกันหรือไม่ ... ถ้าคุณทำแล้วนี่คือโหนดที่ผสาน
//pseudocode
//for the first list
p1=list1;
unsigned int addr[];//to store addresses
i=0;
while(p1!=null){
addr[i]=&p1;
p1=p1->next;
}
int len=sizeof(addr)/sizeof(int);//calculates length of array addr
//for the second list
p2=list2;
while(p2!=null){
if(search(addr[],len,&p2)==1)//match found
{
//this is the merging node
return (p2);
}
p2=p2->next;
}
int search(addr,len,p2){
i=0;
while(i<len){
if(addr[i]==p2)
return 1;
i++;
}
return 0;
}
หวังว่าคงเป็นทางออกที่ถูกต้อง ...
ไม่จำเป็นต้องแก้ไขรายการใด ๆ มีวิธีแก้ปัญหาที่เราต้องสำรวจแต่ละรายการเพียงครั้งเดียว
int FindMergeNode(Node headA, Node headB) {
Node currentA = headA;
Node currentB = headB;
// Do till the two nodes are the same
while (currentA != currentB) {
// If you reached the end of one list start at the beginning of the other
// one currentA
if (currentA.next == null) {
currentA = headA;
} else {
currentA = currentA.next;
}
// currentB
if (currentB.next == null) {
currentB = headB;
} else {
currentB = currentB.next;
}
}
return currentB.data;
}
นี่คือวิธีแก้ปัญหาที่ไร้เดียงสาไม่จำเป็นต้องสำรวจรายการทั้งหมด
หากโหนดที่มีโครงสร้างของคุณมีสามฟิลด์เช่น
struct node {
int data;
int flag; //initially set the flag to zero for all nodes
struct node *next;
};
สมมติว่าคุณมีสองหัว (head1 และ head2) ที่ชี้ไปที่หัวของสองรายการ
สำรวจทั้งสองรายการในจังหวะเดียวกันและใส่ค่าสถานะ = 1 (แฟล็กที่เยี่ยมชม) สำหรับโหนดนั้น
if (node->next->field==1)//possibly longer list will have this opportunity
//this will be your required node.
แล้วสิ่งนี้ล่ะ:
หากคุณได้รับอนุญาตให้สำรวจแต่ละรายการเพียงครั้งเดียวคุณสามารถสร้างโหนดใหม่สำรวจรายการแรกเพื่อให้ทุกโหนดชี้ไปยังโหนดใหม่นี้และสำรวจรายการที่สองเพื่อดูว่าโหนดใดชี้ไปยังโหนดใหม่ของคุณ ( นั่นคือจุดผสานของคุณ) หากการข้ามผ่านครั้งที่สองไม่นำไปสู่โหนดใหม่ของคุณรายการเดิมจะไม่มีจุดผสาน
หากคุณได้รับอนุญาตให้สำรวจรายการมากกว่าหนึ่งครั้งคุณสามารถสำรวจแต่ละรายการเพื่อค้นหาความยาวของเราและหากแตกต่างกันให้ละเว้นโหนด "พิเศษ" ที่จุดเริ่มต้นของรายการที่ยาวขึ้น จากนั้นสำรวจทั้งสองรายการทีละขั้นตอนและค้นหาโหนดแรกที่รวมเข้าด้วยกัน
ขั้นตอนใน Java:
เราสามารถแก้ไขได้อย่างมีประสิทธิภาพโดยการแนะนำช่อง "isVisited" สำรวจรายการแรกและตั้งค่า "isVisited" เป็น "จริง" สำหรับโหนดทั้งหมดจนจบ เริ่มจากวินาทีและค้นหาโหนดแรกที่แฟล็กเป็นจริงและบูมซึ่งเป็นจุดรวมของคุณ
ขั้นตอนที่ 1: ค้นหาความยาวของทั้งสองรายการขั้นตอนที่ 2: ค้นหาความแตกต่างและย้ายรายการที่ใหญ่ที่สุดด้วยความแตกต่างขั้นตอนที่ 3: ตอนนี้ทั้งสองรายการจะอยู่ในตำแหน่งที่ใกล้เคียงกัน ขั้นตอนที่ 4: วนซ้ำตามรายการเพื่อค้นหาจุดผสาน
//Psuedocode
def findmergepoint(list1, list2):
lendiff = list1.length() > list2.length() : list1.length() - list2.length() ? list2.lenght()-list1.lenght()
biggerlist = list1.length() > list2.length() : list1 ? list2 # list with biggest length
smallerlist = list1.length() < list2.length() : list2 ? list1 # list with smallest length
# move the biggest length to the diff position to level both the list at the same position
for i in range(0,lendiff-1):
biggerlist = biggerlist.next
#Looped only once.
while ( biggerlist is not None and smallerlist is not None ):
if biggerlist == smallerlist :
return biggerlist #point of intersection
return None // No intersection found
int FindMergeNode(Node *headA, Node *headB)
{
Node *tempB=new Node;
tempB=headB;
while(headA->next!=NULL)
{
while(tempB->next!=NULL)
{
if(tempB==headA)
return tempB->data;
tempB=tempB->next;
}
headA=headA->next;
tempB=headB;
}
return headA->data;
}
ใช้แผนที่หรือพจนานุกรมเพื่อจัดเก็บ addressess เทียบกับค่าของโหนด หากที่อยู่มีอยู่ในแผนที่ / พจนานุกรมค่าของคีย์คือคำตอบ ฉันทำอย่างนี้:
int FindMergeNode(Node headA, Node headB) {
Map<Object, Integer> map = new HashMap<Object, Integer>();
while(headA != null || headB != null)
{
if(headA != null && map.containsKey(headA.next))
{
return map.get(headA.next);
}
if(headA != null && headA.next != null)
{
map.put(headA.next, headA.next.data);
headA = headA.next;
}
if(headB != null && map.containsKey(headB.next))
{
return map.get(headB.next);
}
if(headB != null && headB.next != null)
{
map.put(headB.next, headB.next.data);
headB = headB.next;
}
}
return 0;
}
โซลูชันความซับซ้อน AO (n) แต่อยู่บนสมมติฐาน.
สมมติฐานคือ: ทั้งสองโหนดมีจำนวนเต็มบวกเท่านั้น
ตรรกะ: ทำให้จำนวนเต็มทั้งหมดของ list1 เป็นลบ จากนั้นเดินไปตาม list2 จนได้จำนวนเต็มลบ เมื่อพบ => นำไปแล้วให้เปลี่ยนเครื่องหมายกลับเป็นบวกและส่งกลับ
static int findMergeNode(SinglyLinkedListNode head1, SinglyLinkedListNode head2) {
SinglyLinkedListNode current = head1; //head1 is give to be not null.
//mark all head1 nodes as negative
while(true){
current.data = -current.data;
current = current.next;
if(current==null) break;
}
current=head2; //given as not null
while(true){
if(current.data<0) return -current.data;
current = current.next;
}
}
เราสามารถใช้พอยน์เตอร์สองตัวและเลื่อนไปมาในลักษณะที่ว่าถ้าพอยน์เตอร์ตัวใดตัวหนึ่งเป็นโมฆะเราจะชี้ไปที่ส่วนหัวของรายการอื่นและเหมือนกันสำหรับอีกรายการด้วยวิธีนี้หากความยาวของรายการต่างกันพวกเขาจะได้พบกันในรอบที่สอง . ถ้าความยาวของ list1 คือ n และ list2 คือ m ความแตกต่างคือ d = abs (nm) พวกเขาจะครอบคลุมระยะทางนี้และพบกันที่จุดผสาน
รหัส:
int findMergeNode(SinglyLinkedListNode* head1, SinglyLinkedListNode* head2) {
SinglyLinkedListNode* start1=head1;
SinglyLinkedListNode* start2=head2;
while (start1!=start2){
start1=start1->next;
start2=start2->next;
if (!start1)
start1=head2;
if (!start2)
start2=head1;
}
return start1->data;
}
คุณสามารถเพิ่มโหนดของlist1
ไปยังแฮชเซ็ตและลูปผ่านวินาทีและหากมีโหนดใด ๆlist2
อยู่ในชุดแล้วถ้าใช่แสดงว่าโหนดผสาน
static int findMergeNode(SinglyLinkedListNode head1, SinglyLinkedListNode head2) {
HashSet<SinglyLinkedListNode> set=new HashSet<SinglyLinkedListNode>();
while(head1!=null)
{
set.add(head1);
head1=head1.next;
}
while(head2!=null){
if(set.contains(head2){
return head2.data;
}
}
return -1;
}
วิธีแก้ไขโดยใช้ javascript
var getIntersectionNode = function(headA, headB) {
if(headA == null || headB == null) return null;
let countA = listCount(headA);
let countB = listCount(headB);
let diff = 0;
if(countA > countB) {
diff = countA - countB;
for(let i = 0; i < diff; i++) {
headA = headA.next;
}
} else if(countA < countB) {
diff = countB - countA;
for(let i = 0; i < diff; i++) {
headB = headB.next;
}
}
return getIntersectValue(headA, headB);
};
function listCount(head) {
let count = 0;
while(head) {
count++;
head = head.next;
}
return count;
}
function getIntersectValue(headA, headB) {
while(headA && headB) {
if(headA === headB) {
return headA;
}
headA = headA.next;
headB = headB.next;
}
return null;
}
หากอนุญาตให้แก้ไขรายการที่เชื่อมโยงได้