直通BAT--数据结构与算法五(链表类)

来源:互联网 发布:辐射4优化补丁 编辑:程序博客网 时间:2024/04/29 02:52
链表题解题技巧:
 
  • 链表调整函数的返回值类型,根据要求往往是节点类型;
  • 处理链表的过程中,先采用画图的方式理清逻辑;
  • 链表问题对于边界条件讨论要求严格;
  • 最好不要原链表的基础上改;
  • 链表赋值时,考虑链表头尾节点,当前节点的next指针是否要为NULL;
  • 善用stack栈结构;
  • node->next ==NULL与node == NULL之间的区别;
  • node = node->next循环前提不要忘记。


链表插入和删除:

  • 特殊处理链表为空,或者链长为1的情况;
  • 注意插入操作的调整过程,前一个节点指向要插入的节点,要插入的节点指向下一个节点;
  • 注意删除操作的调整过程,前一个节点指向下一个节点。
  • 头节点和尾节点特殊考虑

struct ListNode {
     int val;
     struct ListNode *next;
     ListNode(int x) : val(x), next(NULL) {}
};


1.链表节点插入
  
//给定链表的信息,及元素的值A及对应的nxt指向的元素编号同时给定val,请构造出这个环形链表,并插入该值。
//测试样例:
//[1, 3, 4, 5, 7], [1, 2, 3, 4, 0], 2
//返回:{ 1, 2, 3, 4, 5, 7 }


ListNode* InsertValue::insertValue(vector<int> A, vector<int> nxt, int val){

     //创建插入值的节点
     ListNode *insertNode = new ListNode(val);
     //如果A为空
     if (A.size() == 0){
          insertNode->next = insertNode;
          return insertNode;
     }
     //构建链表
     ListNode *headNode = new ListNode(A[0]);
     ListNode *listNode = headNode;
     for (int i = 1; i < A.size(); ++i){
          listNode->next = new ListNode(A[i]);
          listNode = listNode->next;
     }
     listNode->next = headNode;

     //插入的节点值小于原来的头节点
     if (headNode->val >= val){
          insertNode->next = headNode;
          headNode = insertNode;
          return headNode;
     }

     //构建两个节点,如果新插入的节点在这两个节点中间则插入
     ListNode *preNode = headNode;
     ListNode *curNode = headNode->next;

     while (curNode != headNode){
          if (preNode->val <= val && curNode->val >= val){
              preNode->next = new ListNode(val);
              preNode->next->next = curNode;
              return headNode;
          }
          preNode = curNode;
          curNode = curNode->next;
     }

     //新插入的节点比所有节点都大,则插入到最后
     if (preNode->val <= val){
          preNode->next = insertNode;
          insertNode->next = headNode;
          return headNode;
     }
}


2.链表节点删除

//实现一个算法,删除单向链表中间的某个结点。
//给定带删除的头节点和要删除的数字,请执行删除操作,返回删除后的头结点。链表中没有重复数字


//删除链表节点:如果头节点就是,则删除;不是则循环扫描,扫描到节点则将前一个节点指向当前节点的后一个节点
ListNode* RemoveNode::removeNode(ListNode* pHead, int delVal){

     if (pHead->val == delVal){
          pHead = pHead->next;
          return pHead;
     }
     ListNode *preNode = pHead;
     ListNode *curNode = pHead->next;

     while (curNode != NULL){
          if (curNode->val == delVal){
              preNode->next = curNode->next;
              return pHead;
          }
          preNode = curNode;
          curNode = curNode->next;
     }
     return pHead;
}


3.链表分化

//对于一个链表,我们需要用一个特定阈值完成对它的分化,使得小于等于这个值的结点移到前面,大于该值的结点在后面,同时保证两类结点内部的位置关系不变。
//给定一个链表的头结点head,同时给定阈值val,请返回一个链表,使小于等于它的结点在前,大于等于它的在后,保证结点值不重复。
//测试样例:
//{ 1, 4, 2, 5 }, 3
//{1, 2, 4, 5}


//链表分化:定义smallNode存放小于给定值的链表,largeNode存放大于给定值的链表;并初始化
//分别记录其头节点
//给smallNode与largeNode装值
//令largeNode->next=NULL;
//构成完整的链表
ListNode1* ListDivide::listDivide(ListNode* head, int val){

     if (head == NULL || head->next == NULL)
          return head;

     ListNode *node = head;
     ListNode *smallNode = new ListNode(0);
     ListNode *smallNodeHead = smallNode;
     ListNode *largeNode = new ListNode(0);
     ListNode *largeNodeHead = largeNode;

     while (node != NULL){
          if (node->val <= val){
              smallNode->next = node;
              smallNode = smallNode->next;
          }
          else{
              largeNode->next = node;
              largeNode = largeNode->next;
          }
          node = node->next;
     }
     largeNode->next = NULL;

     smallNode->next = largeNodeHead->next;
     head = smallNodeHead->next;

     return head;
}


4.链表查找公共值部分

/现有两个升序链表,且链表中均无重复元素。请设计一个高效的算法,打印两个链表的公共值部分。
//给定两个链表的头指针headA和headB,请返回一个vector,元素为两个链表的公共部分。请保证返回数组的升序。两个链表的元素个数均小于等于500。保证一定有公共值
//测试样例:
//{ 1, 2, 3, 4, 5, 6, 7 }, { 2, 4, 6, 8, 10 }
//返回:[2.4.6]


//链表的公共值部分:判断链表查找值
//相等则headA与headB找下一个节点
//headA值小于headB,则headA等于下一个节点
//headA值大于headB,则headB等于下一个节点
//直到headA与headB有一个为NULL
vector<int> FindCommonParts::findCommonParts(ListNode* headA, ListNode* headB){

     vector<int> commonParts;

     while (headA != NULL && headB != NULL){
          if (headA->val == headB->val){
              commonParts.push_back(headA->val);
              headA = headA->next;
              headB = headB->next;
          }
          else if (headA->val < headB->val){
              headA = headA->next;
          }
          else{
              headB = headB->next;
          }
     }

     return commonParts;
}


5.K值逆序

//有一个单链表,请设计一个算法,使得每K个节点之间逆序,如果最后不够K个节点一组,则不调整最后几个节点。
//例如链表1->2->3->4->5->6->7->8->null,K = 3这个例子。调整后为,3->2->1->6->5->4->7->8->null。
//因为K == 3,所以每三个节点之间逆序,但其中的7,8不调整,因为只有两个节点不够一组。
//
//给定一个单链表的头指针head, 同时给定K值,返回逆序后的链表的头指针。

ListNode* KValueReverse::kValueReverse(ListNode* head, int k){
     //head为NULL,或k为1直接返回
     if (head == NULL || k == 1){
          return head;
     }

     //遍历原链表
     ListNode* curNode = head;
     //记录每一逆序段的头
     ListNode* headNode = head;
     //记录每一逆序段的尾
     ListNode* lastNode = new ListNode(0);
     //记录逆序后的链表
     ListNode* node = new ListNode(0);
     //利用栈结构逆序
     vector<ListNode*> listVector;
     //计数判断
     int count = 0;
     //分离第一次头节点
     int headFlag = 0;

     while (curNode != NULL){
          ++count;
          //入栈操作
          listVector.push_back(curNode);
          //k个节点满
          if (count >= k){
              //记录逆序段的尾
              lastNode = curNode;
              //记录下一个逆序部分的头
              curNode = curNode->next;
              //当前逆序部分的头尾均要设为NULL,防止链表成环
              headNode->next = NULL;
              lastNode->next = NULL;
              //出栈操作
              while (!listVector.empty()){
                   //特殊处理头节点
                   if (headFlag == 0){
                        head = listVector.back();
                        node = head;
                        listVector.pop_back();
                        headFlag = 1;
                   }
                   //处理之后的节点
                   else{
                        node->next = listVector.back();
                        node = node->next;
                        listVector.pop_back();
                   }
              }
              //记录下一逆序段的头
              headNode = curNode;
              count = 0;
              continue;
          }
          //未达到k个节点则继续入栈
          curNode = curNode->next;
     }
     //未满足k个节点,不逆序
     node->next = headNode;

     return head;
}


6.链表删除某个值的所有相等值

//现在有一个单链表。链表中每个节点保存一个整数,再给定一个值val,把所有等于val的节点删掉。
//
//给定一个单链表的头结点head,同时给定一个值val,请返回清除后的链表的头结点,保证链表中有不等于该值的其它值。请保证其他元素的相对顺序。
//
//测试样例:
//{ 1, 2, 3, 4, 3, 2, 1 }, 2
//{1, 3, 4, 3, 1}

//链表删除某个值的所有相等值:将所有不相等的值生成新的链表,最后为NULL
ListNode* ClearValue::clearValue(ListNode* head, int val){
     if (head == NULL)
          return head;

     ListNode* resultHead = NULL;
     ListNode* node = head;
     ListNode* resultNode = NULL;

     while (node != NULL){
          if (node->val != val){
              if (resultHead == NULL){
                   resultHead = node;
                   resultNode = resultHead;
              }
              else{
                   resultNode->next = node;
                   resultNode = resultNode->next;
              }
          }
          node = node->next;
     }
     resultNode->next = NULL;

     return resultHead;
}


7.链表回文


//请编写一个函数,检查链表是否为回文。
//给定一个链表ListNode* pHead,请返回一个bool,代表链表是否为回文。
//测试样例:
//{ 1, 2, 3, 2, 1 }
//返回:true
//{ 1, 2, 2, 1 }
//返回:true
//{ 1, 2, 3, 2, 3 }
//返回:false


//链表是否为回文:定义快慢指针,判断是否快指针到达链表尾:分为奇数偶数回文,慢指针入栈,比较后半部分与前半部分。
bool CheckPalindrome::checkPalindrome(ListNode* pHead){
     //pHead为NULL
     if (pHead == NULL){
          return false;
     }
     //链表只有一个元素
     if (pHead->next == NULL){
          return true;
     }
     //定义快慢指针
     ListNode* slowNode = pHead;
     ListNode* fastNode = pHead;
     vector<int> listVector;
     listVector.push_back(pHead->val);

     //判断是否快指针到达链表尾:分为奇数偶数回文
     while (fastNode->next != NULL){
          if (fastNode->next->next != NULL){
              slowNode = slowNode->next;
              listVector.push_back(slowNode->val);
              fastNode = fastNode->next->next;
          }
          else{
              slowNode = slowNode->next;
              break;
          }
     }
     //比较后半部分与前半部分
     while (!listVector.empty()){
          if (slowNode->val == listVector.back()){
              slowNode = slowNode->next;
              listVector.pop_back();
          }
          else{
              return false;
          }
     }
     return true;

}


8.链表判环

//如何判断一个单链表是否有环?有环的话返回进入环的第一个节点的值,无环的话返回 - 1。
//如果链表的长度为N,请做到时间复杂度O(N),额外空间复杂度O(1)。
//
//给定一个单链表的头结点head(注意另一个参数adjust为加密后的数据调整参数,方便数据设置,与本题求解无关),请返回所求值。

//链表判环:声明一个慢指针一个快指针,快指针走2格,慢指针走1格,直到二者相遇
//然后,快指针指向头节点,慢指针不动,二者循环移动一个节点,直到再次相遇就是入环的第一个点。
int CheckLoop::checkLoop(ListNode* head, int adjust){
     if (head == NULL || head->next == NULL){
          return -1;
     }
     if (head->next->next == NULL){
          return -1;
     }
     if (head->next->next == head){
          return head->val;
     }
     ListNode* slowNode = head->next;
     ListNode* fastNode = head->next->next;

     while (fastNode != NULL && fastNode->next != NULL){
          if (slowNode == fastNode){
              fastNode = head;
              while (slowNode != fastNode){
                   slowNode = slowNode->next;
                   fastNode = fastNode->next;
              }
              return fastNode->val;
          }
          else{
              slowNode = slowNode->next;
              fastNode = fastNode->next->next;
          }
     }

     return -1;
}


9.无环链表相交

//现在有两个无环单链表,若两个链表的长度分别为m和n,请设计一个时间复杂度为O(n + m),额外空间复杂度为O(1)的算法,
//判断这两个链表是否相交。
//给定两个链表的头结点headA和headB,请返回一个bool值,代表这两个链表是否相交。保证两个链表长度小于等于500。

//链表相交:判断最后一个节点是否一样
bool CheckIntersect::checkIntersect(ListNode* headA, ListNode* headB){
     while (headA == NULL || headB == NULL){
          return false;
     }
     while (headA->next != NULL){
          headA = headA->next;
     }
     while (headB->next != NULL){
          headB = headB->next;
     }
     if (headA == headB){
          return true;
     }
     return false;
}


10.有环链表相交

ListNode* circleNode(ListNode* head);

//如何判断两个有环单链表是否相交?相交的话返回第一个相交的节点,不想交的话返回空。
//如果两个链表长度分别为N和M,请做到时间复杂度O(N + M),额外空间复杂度O(1)。
//
//给定两个链表的头结点head1和head2(注意,另外两个参数adjust0和adjust1用于调整数据, 与本题求解无关)。
//请返回一个bool值代表它们是否相交。

ListNode* circleNode(ListNode* head);

bool CheckCircleIntersection::checkCircleIntersection(ListNode* head1, ListNode* head2, int adjust0, int adjust1){

     ListNode* circleNode1 = circleNode(head1);
     ListNode* circleNode2 = circleNode(head2);
     if (circleNode1 == NULL || circleNode2 == NULL){
          return false;
     }
     //判断入环的节点是否相同,相同则在此节点前已相交,否则在环内相交或不想交
     ListNode* node1 = head1;
     ListNode* node2 = head2;
     if (circleNode1 == circleNode2){

          int count1 = 0;
          int count2 = 0;
          while (node1 != circleNode1){
              ++count1;
              node1 = node1->next;
          }
          while (node2 != circleNode2){
              ++count2;
              node2 = node2->next;
          }
          int temp = 0;
          node1 = head1;
          node2 = head2;
          if (count1 > count2){
              temp = count1 - count2;
              while (temp-- > 0){
                   node1 = node1->next;
              }
          }
          else{
              temp = count2 - count1;
              while (temp-- > 0){
                   node2 = node2->next;
              }
          }
          while (node1 != node2){
              node1 = node1->next;
              node2 = node2->next;
          }
          //此时的node1与node2相等,为相交的第一个节点
          return true;
     }
     //环内相交或不想交
     else{
          node1 = circleNode1->next;
          while (node1 != circleNode1){
              if (node1 == circleNode2){
                    //此时的node1与circleNode2相等,为相交的第一个节点
                   return true;
              }
              node1 = node1->next;
          }
          return false;
     }
}

//判断链表是否有环
ListNode* circleNode(ListNode* head){
     if (head == NULL){
          return NULL;
     }
     ListNode* slowNode = head;
     ListNode* fastNode = head;

     while (fastNode != NULL && fastNode->next != NULL){
          slowNode = slowNode->next;
          fastNode = fastNode->next->next;
          if (slowNode == fastNode){
              fastNode = head;
              while (slowNode != fastNode){
                   slowNode = slowNode->next;
                   fastNode = fastNode->next;
              }
              return fastNode;
          }
     }
     return NULL;
}
原创粉丝点击