趁着今天还有点时间,再分享一题,这里有两个题目,
题目一:
给定单项链表的头指针和一个节点指针,定义一个函数在O(1)时间内删除该节点。思路:把非头节点和尾节点的后一位节点的值覆盖到要删除的节点,然后把删除的节点的next指向它下一个节点的下一个节点就OJBK了
如:1->2->3->4 需要删除3,那么把3覆盖到2那里得到 1->3->3->4 然后让删除的节点指向下一个的下一个节点,得到 1->3->(3)->4,括号中就是被删除的节点,这样就能在O(1)的时间内完成链表的删除。
但是这个方法中,如果删除的节点为尾节点,那么后面没有节点能够覆盖删除节点,所以只能从头节点开始查找,一直到尾节点然后删除,所以这个算法的时间复杂度为[(n-1)+O(n)]/n,得到的结果还是为O(1),所以还是满足题目要求。
同时,还需要注意的是,这个算法的前提是基于被删除的节点存在于链表中,否则,需要O(n)的时间来判断是否存在这个节点,那么将不可能满足O(1)的时间限制题目二:
在一个排序的链表中,如何删除重复的节点?
例如:1->2->2->3->3->4->5
删除之后:1->4->5
思路:我们只需要从头遍历整个链表,如果当前节点的值与下一个节点的值相同,那么他们就是重复的节点了,都可以被删除。为了保证删除之后的链表仍然是相连的,我们要把当前节点的前一个节点和后面值比当前节点的值大的节点相连。
我分别用Test1和Test2方法来分别测试题目一和题目二,就目前我能想到的测试用例来说,可以完全通过,如果大家有问题的话,可以在博客下方留言,我下次看到了会和大家讨论,题目二其实就是剑指OFFER上的“删除链表中重复节点”一题,大家可以上牛客网去刷题进行测试,已过。
题目一的测试用例:
- 功能测试(从多个节点的链表中间删除一个节点;从有多个节点的链表中删除头节点;从有多个节点的链表中删除尾节点;从只有一个节点的链表中删除唯一的节点)
- 特殊输入测试(pHead链表的头节点为空;pNode被删除的节点为空)
题目二的测试用例:
- 功能测试(重复的节点位于链表的头部、中间、尾部;链表中没有重复的节点)
- 特殊输入测试(pHead链表的头节点为空;链表中的节点全是重复的)
以下为题目代码:
using System;
namespace 删除链表的结点节点
{
public class ListNode
{
public int val;
public ListNode next;
public ListNode(int x)
{
val = x;
}
}
class Program
{
static void Main(string[] args)
{
Test1();
Test2();
}
public static void Test1()
{
Console.WriteLine("题目一测试案例:");
ListNode list1 = new ListNode(0);
ListNode pNode = list1, deleteNode = list1;
ListNode deleteNode1 = list1, deleteNode2 = null, deleteNode3 = null;
for (int i = 1; i <= 10; i++)
{
ListNode temp = new ListNode(i);
pNode.next = temp;
pNode = pNode.next;
if (i == 5)
deleteNode2 = temp;
if (i == 10)
deleteNode3 = temp;
}
DisplayList(list1);
Console.WriteLine("\n测试一:删除头节点:");
//测试一:删除头节点
DeleteOneNode(list1, deleteNode1);
DisplayList(list1);
Console.WriteLine("\n测试二:删除中间节点:");
//测试二:删除中间节点
DeleteOneNode(list1, deleteNode2);
DisplayList(list1);
Console.WriteLine("\n测试三:删除尾节点:");
//测试三:删除尾节点
DeleteOneNode(list1, deleteNode3);
DisplayList(list1);
Console.WriteLine("\n测试四:删除的节点为空:");
//测试四:删除的节点为空
DeleteOneNode(list1, null);
DisplayList(list1);
Console.WriteLine();
//测试五:链表为空
DeleteOneNode(null, deleteNode1);
Console.WriteLine();
}
public static void Test2()
{
Console.WriteLine("题目二测试案例:");
//删除的节点在中间
ListNode list1 = new ListNode(1);
ListNode pNode = list1;
for (int i = 2; i <= 10; i++)
{
ListNode temp1 = new ListNode(i);
pNode.next = temp1;
pNode = pNode.next;
if (i == 2 || i == 5 || i == 8)
{
ListNode temp2 = new ListNode(i);
pNode.next = temp2;
pNode = pNode.next;
}
}
Console.WriteLine("测试一:");
DisplayList(list1);
Console.WriteLine();
list1 = DeleteDuplication(list1);
DisplayList(list1);
Console.WriteLine();
//删除的节点在头部和尾部
ListNode list2 = new ListNode(1);
pNode = list2;
for (int i = 1; i <= 10; i++)
{
ListNode temp1 = new ListNode(i);
pNode.next = temp1;
pNode = pNode.next;
if (i == 2 || i == 5 || i == 10)
{
ListNode temp2 = new ListNode(i);
pNode.next = temp2;
pNode = pNode.next;
}
}
Console.WriteLine("测试二:");
DisplayList(list2);
Console.WriteLine();
list2 = DeleteDuplication(list2);
DisplayList(list2);
Console.WriteLine();
//删除的链表中没有重复节点
ListNode list3 = new ListNode(1);
pNode = list3;
for (int i = 2; i <= 10; i++)
{
ListNode temp1 = new ListNode(i);
pNode.next = temp1;
pNode = pNode.next;
}
Console.WriteLine("测试三:");
DisplayList(list3);
Console.WriteLine();
list3 = DeleteDuplication(list3);
DisplayList(list3);
Console.WriteLine();
//链表中的节点都是重复的
ListNode list4 = new ListNode(1);
pNode = list4;
for (int i = 1; i <= 10; i++)
{
ListNode temp1 = new ListNode(1);
pNode.next = temp1;
pNode = pNode.next;
}
Console.WriteLine("测试四:");
DisplayList(list4);
Console.WriteLine();
list4 = DeleteDuplication(list4);
DisplayList(list4);
Console.WriteLine();
//链表为空
ListNode list5 = null;
Console.WriteLine("测试五:");
DisplayList(list5);
Console.WriteLine();
list5 = DeleteDuplication(list5);
DisplayList(list5);
Console.WriteLine();
}
/// <summary>
/// 用于删除某一个存在于链表中的节点(限制条件:时间复杂度必须为O(1))
/// </summary>
/// <param name="pHead">链表的头节点</param>
/// <param name="pNode">被删除的节点</param>
/// <returns>返回一个被删除了节点的链表</returns>
public static ListNode DeleteOneNode(ListNode pHead, ListNode pNode)
{
if (pHead == null || pNode == null)
return null;
//不是尾节点
if (pNode.next != null)
{
ListNode pNext = pNode.next;
pNode.val = pNext.val;
pNode.next = pNext.next;
}
//该链表仅一个节点
else if (pHead == pNode)
{
pHead = null;
pNode = null;
}
//要删除的节点为尾节点
else
{
ListNode pTempNode = pHead;
while (pTempNode.next != pNode)
pTempNode = pTempNode.next;
pTempNode.next = null;
pNode = null;
}
return null;
}
/// <summary>
/// 用于删除链表中重复的节点(只要重复了,都删除,如:1->2->3->3->4->4->5,删除之后得:1->2->5)
/// </summary>
/// <param name="pHead">删除的链表的头节点</param>
public static ListNode DeleteDuplication(ListNode pHead)
{
if (pHead == null)
return null;
ListNode preNode = null; //前节点
ListNode pNode = pHead; //当前节点
while (pNode != null)
{
ListNode pNext = pNode.next; //下一个节点
bool needDelete = false; //用于储存判断是否需要删除节点
if (pNext != null && pNode.val == pNext.val)
needDelete = true;
if (!needDelete)
{
preNode = pNode;
pNode = pNext;
}
else
{
int value = pNode.val; //储存重复的节点的值,作用:空间换时间
ListNode pDeleteNode = pNode; //需要删除的节点
while (pDeleteNode != null && pDeleteNode.val == value)
{
pNext = pDeleteNode.next;
pDeleteNode = pNext;
}
if (preNode == null)
pHead = pNext;
else
preNode.next = pNext;
pNode = pNext;
}
}
return pHead;
}
private static void DisplayList(ListNode pHead)
{
if (pHead == null)
{
Console.Write("null");
return;
}
ListNode pNode = pHead;
while (pNode != null)
{
Console.Write(pNode.val + "->");
pNode = pNode.next;
}
}
}
}