LeetCode Reverse Nodes in k-Group Link: https://leetcode.com/problems/reverse-nodes-in-k-group/
Problem description:

Given a linked list, reverse the nodes of a linked list k at a time
and return its modified list.
k is a positive integer and is less than or equal to the length of the
linked list. If the number of nodes is not a multiple of k then
left-out nodes, in the end, should remain as it is.
You may not alter the values in the list's nodes, only nodes
themselves may be changed.
e.g. Input: head = [1,2,3,4,5], k = 3
Output: [3,2,1,4,5]

My solution:
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode** subHead = &amp;head;
        int i = 0;
        for (ListNode* subTail = head; subTail != NULL; subTail = subTail-&gt;next) {
            if ((i+1) % k == 0) {
                ListNode* temp = subTail-&gt;next;
                reverse(subHead, &amp;subTail);
                subTail-&gt;next = temp;
                subHead = &amp;(subTail-&gt;next);
            }
            i++;
        }
        return head;
    }
    
    void reverse(ListNode** subHead, ListNode** subTail) {
        if (*subHead==NULL) return;
          ListNode* itHead = *subHead;
          ListNode* prev = NULL;
          ListNode* next = NULL;
          ListNode* end = (*subTail)-&gt;next;
          while (itHead != end) {
            next = itHead-&gt;next;
            itHead-&gt;next = prev;
            prev = itHead;
            itHead = next;
          }
        std::swap(*subHead, *subTail);
    }
};

This solution was accepted and seemed to do quite well in both space and time. I'm interested in how it can be optimized further, how it can be made more readable, more elegant, or better express good design principles. Note that the data definition and the signature ListNode* reverseKGroup(ListNode* head, int k) is enforced by the question. Feel free to comment on anything you'd like though. Many thanks.
Don't use the macro NULL.  Use the specific keyword nullptr now.
Your struct can be more simply written using immediate inline initializers.
struct ListNode {
    int val = 0;
    ListNode* next = nullptr;
    ListNode() = default;
    ListNode(int x) : val{x} {}
    ListNode(int x, ListNode* next) : val{x}, next{next} {}
};
 

Also note that the * goes with the type in C++, not with the name being declared.
Regarding
void reverse(ListNode** subHead, ListNode** subTail)
It appears that the names subHead and subTail are never modified by the code, so this would be easier if you passed references to the pointers, rather than having an extra explicit dereference operation when you use it.
ListNode* itHead = *subHead;
//...
while (itHead != end) {
    //...
    itHead = next;
}

This is just a for loop that's spread out for no reason.  The value of next you want is lost during the body of the loop, so I can see why you don't put that inside a for construct.  But you can put the declaration of subHead there... put the parts you can into a for loop, even if it's not all three of them.
Re:
if (*subHead==NULL) return;
Don't make explicit comparisons against null.  Use the truth predicates that are part of the (possibly smart!) pointer.
Combining this with the tip of using references (...ListNode*&amp; subHead,) you get simply:  if (!subHead)  return;.
Re:
std::swap(*subHead, *subTail);
This works because you know that these are raw pointers.  But generally you write code like this as a template, or at least you code mostly as if you are writing a template, to ease future maintenance.  If the type of something changes, the code should at best still work; second but still OK is cause a compile-time error that you must fix; but bad is to silently fail.  Consider what would happen if these were not raw pointers but iterators of some type, including something supplied by your program or another library (not in std).
You need to use the so-called &quot;std two-step&quot; to use swap and a few others.  Or, if using C++20 you can use std::ranges::swap instead.  Always use swap etc. properly without relying on knowledge that the overload you want is indeed found in std.