给定存储了n个从大到小排好序的整数,试将任一给定整数 x 插入数组中合适的位置,以保持结果依然有序。分析算法在最坏、最好情况下的时间、空间复杂度。具体来说,不妨假设整数序列存储为一个序列 array,这个序列的结构定义在下面给出,数据存放在数组 data 中。因为数组长度是有限的,我们在此假设 data 数组的最大长度为 kMaxSize。如果在执行插入之前,数组已经满了,则插入失败,返回 false;如果待插入的元素 x 已经在data中,则不要重复插入,返回 false;如果插入成功,则返回 true。
函数接口定义中,ElemType是用户定义的数据类型,例如 int、double 或者 char 等。函数 Find 的功能是,返回带空头结点的单链表 list 的倒数第 m 个元素的值,并且不得对 list 做任何改变。如果这样的元素不存在,则返回一个错误标志 ERROR(这个标志的值由裁判程序定义)。
裁判测试程序样例:
intmain() { LinkedList<int> mylist; int n, d; std::cin >> n; try { for(int i=1; i<=n; i++) { std::cin >> d; mylist.Insert(i,d); } } catch(LinkedList<int>::ListException e) { std::cout << e.error_ <<std::endl; } int m; std::cin >> m; std::cout << mylist.Find(m) << std::endl; for (int i = 1; i <= mylist.Length(); i++) { std::cout << mylist.Get(i) << (i == mylist.Length() ? '\n' : ' '); } return0; }
输入样例:
在这里给出一组输入。例如:
5 1 2 4 5 6 3
输出样例:
在这里给出相应的输出。例如:
4 1 2 4 5 6
解析
template <typename ElemType> ElemType LinkedList<ElemType>::Find(int m) { // 1. 检查m的有效性 if (m <= 0) { // 抛出异常:m必须大于0 throw ListException("Error: m must be greater than 0."); } // 初始化快慢指针,都指向第一个数据节点 ListNode* fast = head_->next_; ListNode* slow = head_->next_;
// 2. 快指针先行 m-1 步 // fast 先走 m-1 步,如果走到头了,说明链表长度 L < m for (int i = 0; i < m; ++i) { if (fast == nullptr) { // fast在移动 m 步前就遇到了 nullptr,说明 L < m。 // 抛出异常:倒数第 m 个元素不存在 throw ListException("Error: List length is less than m. Element does not exist."); } fast = fast->next_; }
// 3. 快慢指针同步移动 // 现在 fast 领先 slow m 个节点。 // 当 fast 指向 nullptr 时 (走到链表尾的下一个),slow 就指向倒数第 m 个节点。 while (fast != nullptr) { slow = slow->next_; fast = fast->next_; }
// 4. 返回结果 // 由于 m <= L 已经在上面检查过,此时 slow 不会是 nullptr // 但为了代码的健壮性,我们可以再做一次检查(通常不是必须的) if (slow == nullptr) { // 理论上不会发生,除非链表在操作过程中被修改 throw ListException("Internal Error: Unexpected null pointer."); }
return slow->data_; }
注意
I.
作者第一次代码
template <typename ElemType> ElemType LinkedList<ElemType>::Find(int m) { if (m < 1 || m > length_ || m == 0) { return -1; } Position p = head_->next_; Position q = head_->next_; for (int i = 1; i < m; i++) { p = p->next_; } while (p->next_ != nullptr) { p = p->next_; q = q->next_; } return q->data_; }
会出现19分的情况。
经过豆包、deepseek的深度思考还是19分
豆包:
template <typename ElemType> ElemType LinkedList<ElemType>::Find(int m) { // 处理 m 不合法或链表长度不足的情况 if (m < 1 || m > length_) { return ERROR; // 确保 ERROR 在当前作用域可见 } Position p = head_->next_; // 快指针,先移动 m-1 步 Position q = head_->next_; // 慢指针,最终指向倒数第 m 项 // 快指针先移动 m-1 步 for (int i = 1; i < m; ++i) { p = p->next_; } // 快慢指针同时移动,直到快指针到达最后一个节点 while (p->next_ != nullptr) { p = p->next_; q = q->next_; } return q->data_; }
deepseek:
template <typename ElemType> ElemType LinkedList<ElemType>::Find(int m) { // 检查边界条件 if (m < 1 || m > length_) { // 这里应该返回错误标志,但裁判程序没有定义ERROR // 根据测试用例,返回-1可能是可以的 return -1; } Position p = head_->next_; Position q = head_->next_; // 先让p指针向前移动m-1步 for (int i = 1; i < m; i++) { if (p == nullptr) { return -1; // 额外保护,虽然理论上不会发生 } p = p->next_; } // 然后p和q同时移动,直到p到达最后一个节点 while (p->next_ != nullptr) { p = p->next_; q = q->next_; } return q->data_; }
struct ListException 是一个自定义异常类型(C++ 中 struct 和 class 主要区别是默认访问权限,这里用 struct 也能实现类的功能)。其核心作用是:当链表操作(如找倒数第 m 个元素、插入元素等)遇到非法情况(如 m 不合法、链表为空等)时,抛出包含错误描述的异常对象,让上层代码(如 main 函数)能捕获并处理错误,而非让程序直接崩溃。